summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Threading
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Threading')
-rw-r--r--src/mscorlib/src/System/Threading/AbandonedMutexException.cs85
-rw-r--r--src/mscorlib/src/System/Threading/ApartmentState.cs28
-rw-r--r--src/mscorlib/src/System/Threading/AsyncLocal.cs116
-rw-r--r--src/mscorlib/src/System/Threading/AutoResetEvent.cs27
-rw-r--r--src/mscorlib/src/System/Threading/CancellationToken.cs499
-rw-r--r--src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs161
-rw-r--r--src/mscorlib/src/System/Threading/CancellationTokenSource.cs1269
-rw-r--r--src/mscorlib/src/System/Threading/CountdownEvent.cs592
-rw-r--r--src/mscorlib/src/System/Threading/EventResetMode.cs26
-rw-r--r--src/mscorlib/src/System/Threading/EventWaitHandle.cs297
-rw-r--r--src/mscorlib/src/System/Threading/ExecutionContext.cs1398
-rw-r--r--src/mscorlib/src/System/Threading/IObjectHandle.cs31
-rw-r--r--src/mscorlib/src/System/Threading/Interlocked.cs233
-rw-r--r--src/mscorlib/src/System/Threading/LazyInitializer.cs265
-rw-r--r--src/mscorlib/src/System/Threading/LockCookie.cs57
-rw-r--r--src/mscorlib/src/System/Threading/LockRecursionException.cs34
-rw-r--r--src/mscorlib/src/System/Threading/ManualResetEvent.cs27
-rw-r--r--src/mscorlib/src/System/Threading/ManualResetEventSlim.cs813
-rw-r--r--src/mscorlib/src/System/Threading/Monitor.cs256
-rw-r--r--src/mscorlib/src/System/Threading/Mutex.cs488
-rw-r--r--src/mscorlib/src/System/Threading/Overlapped.cs423
-rw-r--r--src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs24
-rw-r--r--src/mscorlib/src/System/Threading/ReaderWriterLock.cs306
-rw-r--r--src/mscorlib/src/System/Threading/Semaphore.cs201
-rw-r--r--src/mscorlib/src/System/Threading/SemaphoreFullException.cs30
-rw-r--r--src/mscorlib/src/System/Threading/SemaphoreSlim.cs930
-rw-r--r--src/mscorlib/src/System/Threading/SendOrPostCallback.cs16
-rw-r--r--src/mscorlib/src/System/Threading/SpinLock.cs750
-rw-r--r--src/mscorlib/src/System/Threading/SpinWait.cs369
-rw-r--r--src/mscorlib/src/System/Threading/SynchronizationContext.cs320
-rw-r--r--src/mscorlib/src/System/Threading/SynchronizationLockException.cs44
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs295
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs157
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs795
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs2303
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs101
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/Parallel.cs3594
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ParallelLoopState.cs642
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ParallelRangeManager.cs278
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs553
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs763
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/Task.cs7344
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs93
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs370
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs867
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs424
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs3206
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs757
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs81
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs189
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs139
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/future.cs1667
-rw-r--r--src/mscorlib/src/System/Threading/Thread.cs1756
-rw-r--r--src/mscorlib/src/System/Threading/ThreadAbortException.cs46
-rw-r--r--src/mscorlib/src/System/Threading/ThreadInterruptedException.cs41
-rw-r--r--src/mscorlib/src/System/Threading/ThreadLocal.cs815
-rw-r--r--src/mscorlib/src/System/Threading/ThreadPool.cs1954
-rw-r--r--src/mscorlib/src/System/Threading/ThreadPriority.cs32
-rw-r--r--src/mscorlib/src/System/Threading/ThreadStart.cs25
-rw-r--r--src/mscorlib/src/System/Threading/ThreadStartException.cs37
-rw-r--r--src/mscorlib/src/System/Threading/ThreadState.cs36
-rw-r--r--src/mscorlib/src/System/Threading/ThreadStateException.cs41
-rw-r--r--src/mscorlib/src/System/Threading/Timeout.cs21
-rw-r--r--src/mscorlib/src/System/Threading/Timer.cs954
-rw-r--r--src/mscorlib/src/System/Threading/Volatile.cs441
-rw-r--r--src/mscorlib/src/System/Threading/WaitHandle.cs617
-rw-r--r--src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs40
-rw-r--r--src/mscorlib/src/System/Threading/WaitHandleExtensions.cs46
68 files changed, 41635 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Threading/AbandonedMutexException.cs b/src/mscorlib/src/System/Threading/AbandonedMutexException.cs
new file mode 100644
index 0000000000..6b4977fbc5
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/AbandonedMutexException.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+//
+// AbandonedMutexException
+// Thrown when a wait completes because one or more mutexes was abandoned.
+// AbandonedMutexs indicate serious error in user code or machine state.
+////////////////////////////////////////////////////////////////////////////////
+
+namespace System.Threading {
+
+ using System;
+ using System.Runtime.Serialization;
+ using System.Threading;
+ using System.Runtime.InteropServices;
+
+ [Serializable]
+ [ComVisibleAttribute(false)]
+ public class AbandonedMutexException : SystemException {
+
+ private int m_MutexIndex = -1;
+ private Mutex m_Mutex = null;
+
+ public AbandonedMutexException()
+ : base(Environment.GetResourceString("Threading.AbandonedMutexException")) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ }
+
+ public AbandonedMutexException(String message)
+ : base(message) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ }
+
+ public AbandonedMutexException(String message, Exception inner )
+ : base(message, inner) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ }
+
+ public AbandonedMutexException(int location, WaitHandle handle)
+ : base(Environment.GetResourceString("Threading.AbandonedMutexException")) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ SetupException(location,handle);
+ }
+
+ public AbandonedMutexException(String message,int location, WaitHandle handle)
+ : base(message) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ SetupException(location,handle);
+ }
+
+ public AbandonedMutexException(String message, Exception inner,int location, WaitHandle handle )
+ : base(message, inner) {
+ SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX);
+ SetupException(location,handle);
+ }
+
+ private void SetupException(int location, WaitHandle handle)
+ {
+ m_MutexIndex = location;
+ if(handle != null)
+ m_Mutex = handle as Mutex;
+ }
+
+ protected AbandonedMutexException(SerializationInfo info, StreamingContext context) : base(info, context) {
+ }
+
+ public Mutex Mutex
+ {
+ get {
+ return m_Mutex;
+ }
+ }
+
+ public int MutexIndex
+ {
+ get{
+ return m_MutexIndex;
+ }
+ }
+
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/ApartmentState.cs b/src/mscorlib/src/System/Threading/ApartmentState.cs
new file mode 100644
index 0000000000..844f85e9e7
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ApartmentState.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Enum to represent the different threading models
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+
+ [Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public enum ApartmentState
+ {
+ /*=========================================================================
+ ** Constants for thread apartment states.
+ =========================================================================*/
+ STA = 0,
+ MTA = 1,
+ Unknown = 2
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/AsyncLocal.cs b/src/mscorlib/src/System/Threading/AsyncLocal.cs
new file mode 100644
index 0000000000..264f2a6ff7
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/AsyncLocal.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Security;
+
+namespace System.Threading
+{
+ //
+ // AsyncLocal<T> represents "ambient" data that is local to a given asynchronous control flow, such as an
+ // async method. For example, say you want to associate a culture with a given async flow:
+ //
+ // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>();
+ //
+ // static async Task SomeOperationAsync(Culture culture)
+ // {
+ // s_currentCulture.Value = culture;
+ //
+ // await FooAsync();
+ // }
+ //
+ // static async Task FooAsync()
+ // {
+ // PrintStringWithCulture(s_currentCulture.Value);
+ // }
+ //
+ // AsyncLocal<T> also provides optional notifications when the value associated with the current thread
+ // changes, either because it was explicitly changed by setting the Value property, or implicitly changed
+ // when the thread encountered an "await" or other context transition. For example, we might want our
+ // current culture to be communicated to the OS as well:
+ //
+ // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
+ // args =>
+ // {
+ // NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
+ // });
+ //
+ public sealed class AsyncLocal<T> : IAsyncLocal
+ {
+ [SecurityCritical] // critical because this action will terminate the process if it throws.
+ private readonly Action<AsyncLocalValueChangedArgs<T>> m_valueChangedHandler;
+
+ //
+ // Constructs an AsyncLocal<T> that does not receive change notifications.
+ //
+ public AsyncLocal()
+ {
+ }
+
+ //
+ // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes
+ // on any thread.
+ //
+ [SecurityCritical]
+ public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler)
+ {
+ m_valueChangedHandler = valueChangedHandler;
+ }
+
+ public T Value
+ {
+ [SecuritySafeCritical]
+ get
+ {
+ object obj = ExecutionContext.GetLocalValue(this);
+ return (obj == null) ? default(T) : (T)obj;
+ }
+ [SecuritySafeCritical]
+ set
+ {
+ ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null);
+ }
+ }
+
+ [SecurityCritical]
+ void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged)
+ {
+ Contract.Assert(m_valueChangedHandler != null);
+ T previousValue = previousValueObj == null ? default(T) : (T)previousValueObj;
+ T currentValue = currentValueObj == null ? default(T) : (T)currentValueObj;
+ m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged));
+ }
+ }
+
+ //
+ // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type.
+ //
+ internal interface IAsyncLocal
+ {
+ [SecurityCritical]
+ void OnValueChanged(object previousValue, object currentValue, bool contextChanged);
+ }
+
+ public struct AsyncLocalValueChangedArgs<T>
+ {
+ public T PreviousValue { get; private set; }
+ public T CurrentValue { get; private set; }
+
+ //
+ // If the value changed because we changed to a different ExecutionContext, this is true. If it changed
+ // because someone set the Value property, this is false.
+ //
+ public bool ThreadContextChanged { get; private set; }
+
+ internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged)
+ : this()
+ {
+ PreviousValue = previousValue;
+ CurrentValue = currentValue;
+ ThreadContextChanged = contextChanged;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/AutoResetEvent.cs b/src/mscorlib/src/System/Threading/AutoResetEvent.cs
new file mode 100644
index 0000000000..6fe6c06232
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/AutoResetEvent.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: An example of a WaitHandle class
+**
+**
+=============================================================================*/
+namespace System.Threading {
+
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime.InteropServices;
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class AutoResetEvent : EventWaitHandle
+ {
+ public AutoResetEvent(bool initialState) : base(initialState,EventResetMode.AutoReset){ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/CancellationToken.cs b/src/mscorlib/src/System/Threading/CancellationToken.cs
new file mode 100644
index 0000000000..48a2344c31
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/CancellationToken.cs
@@ -0,0 +1,499 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#pragma warning disable 0420 // turn off 'a reference to a volatile field will not be treated as volatile' during CAS.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Security;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Propagates notification that operations should be canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A <see cref="CancellationToken"/> may be created directly in an unchangeable canceled or non-canceled state
+ /// using the CancellationToken's constructors. However, to have a CancellationToken that can change
+ /// from a non-canceled to a canceled state,
+ /// <see cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see> must be used.
+ /// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its
+ /// <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property.
+ /// </para>
+ /// <para>
+ /// Once canceled, a token may not transition to a non-canceled state, and a token whose
+ /// <see cref="CanBeCanceled"/> is false will never change to one that can be canceled.
+ /// </para>
+ /// <para>
+ /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
+ /// </para>
+ /// </remarks>
+ [ComVisible(false)]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
+ public struct CancellationToken
+ {
+ // The backing TokenSource.
+ // if null, it implicitly represents the same thing as new CancellationToken(false).
+ // When required, it will be instantiated to reflect this.
+ private CancellationTokenSource m_source;
+ //!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid
+
+ /* Properties */
+
+ /// <summary>
+ /// Returns an empty CancellationToken value.
+ /// </summary>
+ /// <remarks>
+ /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
+ /// </remarks>
+ public static CancellationToken None
+ {
+ get { return default(CancellationToken); }
+ }
+
+ /// <summary>
+ /// Gets whether cancellation has been requested for this token.
+ /// </summary>
+ /// <value>Whether cancellation has been requested for this token.</value>
+ /// <remarks>
+ /// <para>
+ /// This property indicates whether cancellation has been requested for this token,
+ /// either through the token initially being construted in a canceled state, or through
+ /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
+ /// on the token's associated <see cref="CancellationTokenSource"/>.
+ /// </para>
+ /// <para>
+ /// If this property is true, it only guarantees that cancellation has been requested.
+ /// It does not guarantee that every registered handler
+ /// has finished executing, nor that cancellation requests have finished propagating
+ /// to all registered handlers. Additional synchronization may be required,
+ /// particularly in situations where related objects are being canceled concurrently.
+ /// </para>
+ /// </remarks>
+ public bool IsCancellationRequested
+ {
+ get
+ {
+ return m_source != null && m_source.IsCancellationRequested;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether this token is capable of being in the canceled state.
+ /// </summary>
+ /// <remarks>
+ /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
+ /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
+ /// return true.
+ /// </remarks>
+ public bool CanBeCanceled
+ {
+ get
+ {
+ return m_source != null && m_source.CanBeCanceled;
+ }
+ }
+
+ /// <summary>
+ /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
+ /// <remarks>
+ /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
+ /// to be instantiated. It is preferable to only use this property when necessary, and to then
+ /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
+ /// the source will dispose of this allocated handle). The handle should not be closed or disposed directly.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public WaitHandle WaitHandle
+ {
+ get
+ {
+ if (m_source == null)
+ {
+ InitializeDefaultSource();
+ }
+
+ return m_source.WaitHandle;
+ }
+ }
+
+ // public CancellationToken()
+ // this constructor is implicit for structs
+ // -> this should behaves exactly as for new CancellationToken(false)
+
+ /// <summary>
+ /// Internal constructor only a CancellationTokenSource should create a CancellationToken
+ /// </summary>
+ internal CancellationToken(CancellationTokenSource source)
+ {
+ m_source = source;
+ }
+
+ /// <summary>
+ /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </summary>
+ /// <param name="canceled">
+ /// The canceled state for the token.
+ /// </param>
+ /// <remarks>
+ /// Tokens created with this constructor will remain in the canceled state specified
+ /// by the <paramref name="canceled"/> parameter. If <paramref name="canceled"/> is false,
+ /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
+ /// If <paramref name="canceled"/> is true,
+ /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true.
+ /// </remarks>
+ public CancellationToken(bool canceled) :
+ this()
+ {
+ if(canceled)
+ m_source = CancellationTokenSource.InternalGetStaticSource(canceled);
+ }
+
+ /* Methods */
+
+
+ private readonly static Action<Object> s_ActionToActionObjShunt = new Action<Object>(ActionToActionObjShunt);
+ private static void ActionToActionObjShunt(object obj)
+ {
+ Action action = obj as Action;
+ Contract.Assert(action != null, "Expected an Action here");
+ action();
+ }
+
+ /// <summary>
+ /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// If this token is already in the canceled state, the
+ /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
+ /// propagated out of this method call.
+ /// </para>
+ /// <para>
+ /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+ /// along with the delegate and will be used when executing it.
+ /// </para>
+ /// </remarks>
+ /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+ /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
+ /// be used to deregister the callback.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+ public CancellationTokenRegistration Register(Action callback)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ return Register(
+ s_ActionToActionObjShunt,
+ callback,
+ false, // useSync=false
+ true // useExecutionContext=true
+ );
+ }
+
+ /// <summary>
+ /// Registers a delegate that will be called when this
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// If this token is already in the canceled state, the
+ /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
+ /// propagated out of this method call.
+ /// </para>
+ /// <para>
+ /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+ /// along with the delegate and will be used when executing it.
+ /// </para>
+ /// </remarks>
+ /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+ /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
+ /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
+ /// when invoking the <paramref name="callback"/>.</param>
+ /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
+ /// be used to deregister the callback.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+ public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ return Register(
+ s_ActionToActionObjShunt,
+ callback,
+ useSynchronizationContext,
+ true // useExecutionContext=true
+ );
+ }
+
+ /// <summary>
+ /// Registers a delegate that will be called when this
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// If this token is already in the canceled state, the
+ /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
+ /// propagated out of this method call.
+ /// </para>
+ /// <para>
+ /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+ /// along with the delegate and will be used when executing it.
+ /// </para>
+ /// </remarks>
+ /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+ /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
+ /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
+ /// be used to deregister the callback.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+ public CancellationTokenRegistration Register(Action<Object> callback, Object state)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ return Register(
+ callback,
+ state,
+ false, // useSync=false
+ true // useExecutionContext=true
+ );
+ }
+
+ /// <summary>
+ /// Registers a delegate that will be called when this
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// If this token is already in the canceled state, the
+ /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
+ /// propagated out of this method call.
+ /// </para>
+ /// <para>
+ /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists,
+ /// will be captured along with the delegate and will be used when executing it.
+ /// </para>
+ /// </remarks>
+ /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+ /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
+ /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
+ /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
+ /// when invoking the <paramref name="callback"/>.</param>
+ /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
+ /// be used to deregister the callback.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext)
+ {
+ return Register(
+ callback,
+ state,
+ useSynchronizationContext,
+ true // useExecutionContext=true
+ );
+ }
+
+ // helper for internal registration needs that don't require an EC capture (e.g. creating linked token sources, or registering unstarted TPL tasks)
+ // has a handy signature, and skips capturing execution context.
+ internal CancellationTokenRegistration InternalRegisterWithoutEC(Action<object> callback, Object state)
+ {
+ return Register(
+ callback,
+ state,
+ false, // useSyncContext=false
+ false // useExecutionContext=false
+ );
+ }
+
+ // the real work..
+ [SecuritySafeCritical]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext, bool useExecutionContext)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ if (CanBeCanceled == false)
+ {
+ return new CancellationTokenRegistration(); // nothing to do for tokens than can never reach the canceled state. Give them a dummy registration.
+ }
+
+ // Capture sync/execution contexts if required.
+ // Note: Only capture sync/execution contexts if IsCancellationRequested = false
+ // as we know that if it is true that the callback will just be called synchronously.
+
+ SynchronizationContext capturedSyncContext = null;
+ ExecutionContext capturedExecutionContext = null;
+ if (!IsCancellationRequested)
+ {
+ if (useSynchronizationContext)
+ capturedSyncContext = SynchronizationContext.Current;
+ if (useExecutionContext)
+ capturedExecutionContext = ExecutionContext.Capture(
+ ref stackMark, ExecutionContext.CaptureOptions.OptimizeDefaultCase); // ideally we'd also use IgnoreSyncCtx, but that could break compat
+ }
+
+ // Register the callback with the source.
+ return m_source.InternalRegister(callback, state, capturedSyncContext, capturedExecutionContext);
+ }
+
+ /// <summary>
+ /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
+ /// specified token.
+ /// </summary>
+ /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
+ /// instance.</param>
+ /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
+ /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
+ /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
+ public bool Equals(CancellationToken other)
+ {
+ //if both sources are null, then both tokens represent the Empty token.
+ if (m_source == null && other.m_source == null)
+ {
+ return true;
+ }
+
+ // one is null but other has inflated the default source
+ // these are only equal if the inflated one is the staticSource(false)
+ if (m_source == null)
+ {
+ return other.m_source == CancellationTokenSource.InternalGetStaticSource(false);
+ }
+
+ if (other.m_source == null)
+ {
+ return m_source == CancellationTokenSource.InternalGetStaticSource(false);
+ }
+
+ // general case, we check if the sources are identical
+
+ return m_source == other.m_source;
+ }
+
+ /// <summary>
+ /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
+ /// specified <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <param name="other">The other object to which to compare this instance.</param>
+ /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
+ /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
+ /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
+ /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
+ /// <exception cref="T:System.ObjectDisposedException">An associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public override bool Equals(Object other)
+ {
+ if (other is CancellationToken)
+ {
+ return Equals((CancellationToken) other);
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </summary>
+ /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
+ public override Int32 GetHashCode()
+ {
+ if (m_source == null)
+ {
+ // link to the common source so that we have a source to interrogate.
+ return CancellationTokenSource.InternalGetStaticSource(false).GetHashCode();
+ }
+
+ return m_source.GetHashCode();
+ }
+
+ /// <summary>
+ /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
+ /// </summary>
+ /// <param name="left">The first instance.</param>
+ /// <param name="right">The second instance.</param>
+ /// <returns>True if the instances are equal; otherwise, false.</returns>
+ /// <exception cref="T:System.ObjectDisposedException">An associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public static bool operator ==(CancellationToken left, CancellationToken right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
+ /// </summary>
+ /// <param name="left">The first instance.</param>
+ /// <param name="right">The second instance.</param>
+ /// <returns>True if the instances are not equal; otherwise, false.</returns>
+ /// <exception cref="T:System.ObjectDisposedException">An associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public static bool operator !=(CancellationToken left, CancellationToken right)
+ {
+ return !left.Equals(right);
+ }
+
+ /// <summary>
+ /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
+ /// this token has had cancellation requested.
+ /// </summary>
+ /// <remarks>
+ /// This method provides functionality equivalent to:
+ /// <code>
+ /// if (token.IsCancellationRequested)
+ /// throw new OperationCanceledException(token);
+ /// </code>
+ /// </remarks>
+ /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The associated <see
+ /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+ public void ThrowIfCancellationRequested()
+ {
+ if (IsCancellationRequested)
+ ThrowOperationCanceledException();
+ }
+
+ // Throw an ODE if this CancellationToken's source is disposed.
+ internal void ThrowIfSourceDisposed()
+ {
+ if ((m_source != null) && m_source.IsDisposed)
+ ThrowObjectDisposedException();
+ }
+
+ // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
+ private void ThrowOperationCanceledException()
+ {
+ throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this);
+ }
+
+ private static void ThrowObjectDisposedException()
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationToken_SourceDisposed"));
+ }
+
+ // -----------------------------------
+ // Private helpers
+
+ private void InitializeDefaultSource()
+ {
+ // Lazy is slower, and although multiple threads may try and set m_source repeatedly, the race condition is benign.
+ // Alternative: LazyInititalizer.EnsureInitialized(ref m_source, ()=>CancellationTokenSource.InternalGetStaticSource(false));
+
+ m_source = CancellationTokenSource.InternalGetStaticSource(false);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs
new file mode 100644
index 0000000000..34e0bb0aba
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs
@@ -0,0 +1,161 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+////////////////////////////////////////////////////////////////////////////////
+
+using System.Diagnostics.Contracts;
+using System.Security.Permissions;
+using System.Runtime.CompilerServices;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Represents a callback delegate that has been registered with a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </summary>
+ /// <remarks>
+ /// To unregister a callback, dispose the corresponding Registration instance.
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public struct CancellationTokenRegistration : IEquatable<CancellationTokenRegistration>, IDisposable
+ {
+ private readonly CancellationCallbackInfo m_callbackInfo;
+ private readonly SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> m_registrationInfo;
+
+ internal CancellationTokenRegistration(
+ CancellationCallbackInfo callbackInfo,
+ SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> registrationInfo)
+ {
+ m_callbackInfo = callbackInfo;
+ m_registrationInfo = registrationInfo;
+ }
+
+ /// <summary>
+ /// Attempts to deregister the item. If it's already being run, this may fail.
+ /// Entails a full memory fence.
+ /// </summary>
+ /// <returns>True if the callback was found and deregistered, false otherwise.</returns>
+ [FriendAccessAllowed]
+ internal bool TryDeregister()
+ {
+ if (m_registrationInfo.Source == null) //can be null for dummy registrations.
+ return false;
+
+ // Try to remove the callback info from the array.
+ // It is possible the callback info is missing (removed for run, or removed by someone else)
+ // It is also possible there is info in the array but it doesn't match our current registration's callback info.
+ CancellationCallbackInfo prevailingCallbackInfoInSlot = m_registrationInfo.Source.SafeAtomicRemove(m_registrationInfo.Index, m_callbackInfo);
+
+ if (prevailingCallbackInfoInSlot != m_callbackInfo)
+ return false; //the callback in the slot wasn't us.
+
+ return true;
+ }
+
+ /// <summary>
+ /// Disposes of the registration and unregisters the target callback from the associated
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// If the target callback is currently executing this method will wait until it completes, except
+ /// in the degenerate cases where a callback method deregisters itself.
+ /// </summary>
+ public void Dispose()
+ {
+ // Remove the entry from the array.
+ // This call includes a full memory fence which prevents potential reorderings of the reads below
+ bool deregisterOccurred = TryDeregister();
+
+ // We guarantee that we will not return if the callback is being executed (assuming we are not currently called by the callback itself)
+ // We achieve this by the following rules:
+ // 1. if we are called in the context of an executing callback, no need to wait (determined by tracking callback-executor threadID)
+ // - if the currently executing callback is this CTR, then waiting would deadlock. (We choose to return rather than deadlock)
+ // - if not, then this CTR cannot be the one executing, hence no need to wait
+ //
+ // 2. if deregistration failed, and we are on a different thread, then the callback may be running under control of cts.Cancel()
+ // => poll until cts.ExecutingCallback is not the one we are trying to deregister.
+
+ var callbackInfo = m_callbackInfo;
+ if (callbackInfo != null)
+ {
+ var tokenSource = callbackInfo.CancellationTokenSource;
+ if (tokenSource.IsCancellationRequested && //running callbacks has commenced.
+ !tokenSource.IsCancellationCompleted && //running callbacks hasn't finished
+ !deregisterOccurred && //deregistration failed (ie the callback is missing from the list)
+ tokenSource.ThreadIDExecutingCallbacks != Thread.CurrentThread.ManagedThreadId) //the executingThreadID is not this threadID.
+ {
+ // Callback execution is in progress, the executing thread is different to us and has taken the callback for execution
+ // so observe and wait until this target callback is no longer the executing callback.
+ tokenSource.WaitForCallbackToComplete(m_callbackInfo);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Determines whether two <see
+ /// cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see>
+ /// instances are equal.
+ /// </summary>
+ /// <param name="left">The first instance.</param>
+ /// <param name="right">The second instance.</param>
+ /// <returns>True if the instances are equal; otherwise, false.</returns>
+ public static bool operator ==(CancellationTokenRegistration left, CancellationTokenRegistration right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Determines whether two <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> instances are not equal.
+ /// </summary>
+ /// <param name="left">The first instance.</param>
+ /// <param name="right">The second instance.</param>
+ /// <returns>True if the instances are not equal; otherwise, false.</returns>
+ public static bool operator !=(CancellationTokenRegistration left, CancellationTokenRegistration right)
+ {
+ return !left.Equals(right);
+ }
+
+ /// <summary>
+ /// Determines whether the current <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> instance is equal to the
+ /// specified <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <param name="obj">The other object to which to compare this instance.</param>
+ /// <returns>True, if both this and <paramref name="obj"/> are equal. False, otherwise.
+ /// Two <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> instances are equal if
+ /// they both refer to the output of a single call to the same Register method of a
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </returns>
+ public override bool Equals(object obj)
+ {
+ return ((obj is CancellationTokenRegistration) && Equals((CancellationTokenRegistration) obj));
+ }
+
+ /// <summary>
+ /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
+ /// specified <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <param name="other">The other <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> to which to compare this instance.</param>
+ /// <returns>True, if both this and <paramref name="other"/> are equal. False, otherwise.
+ /// Two <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> instances are equal if
+ /// they both refer to the output of a single call to the same Register method of a
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </returns>
+ public bool Equals(CancellationTokenRegistration other)
+ {
+ return m_callbackInfo == other.m_callbackInfo &&
+ m_registrationInfo.Source == other.m_registrationInfo.Source &&
+ m_registrationInfo.Index == other.m_registrationInfo.Index;
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration.</see>.
+ /// </summary>
+ /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationTokenRegistration">CancellationTokenRegistration</see> instance.</returns>
+ public override int GetHashCode()
+ {
+ if (m_registrationInfo.Source != null)
+ return m_registrationInfo.Source.GetHashCode() ^ m_registrationInfo.Index.GetHashCode();
+
+ return m_registrationInfo.Index.GetHashCode();
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/CancellationTokenSource.cs b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs
new file mode 100644
index 0000000000..954cd38344
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs
@@ -0,0 +1,1269 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+//
+////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Security;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+using System.Runtime;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Signals to a <see cref="System.Threading.CancellationToken"/> that it should be canceled.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// <see cref="T:System.Threading.CancellationTokenSource"/> is used to instantiate a <see
+ /// cref="T:System.Threading.CancellationToken"/>
+ /// (via the source's <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property)
+ /// that can be handed to operations that wish to be notified of cancellation or that can be used to
+ /// register asynchronous operations for cancellation. That token may have cancellation requested by
+ /// calling to the source's <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
+ /// method.
+ /// </para>
+ /// <para>
+ /// All members of this class, except <see cref="Dispose">Dispose</see>, are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </para>
+ /// </remarks>
+ [ComVisible(false)]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+
+ public class CancellationTokenSource : IDisposable
+ {
+ //static sources that can be used as the backing source for 'fixed' CancellationTokens that never change state.
+ private static readonly CancellationTokenSource _staticSource_Set = new CancellationTokenSource(true);
+ private static readonly CancellationTokenSource _staticSource_NotCancelable = new CancellationTokenSource(false);
+
+ //Note: the callback lists array is only created on first registration.
+ // the actual callback lists are only created on demand.
+ // Storing a registered callback costs around >60bytes, hence some overhead for the lists array is OK
+ // At most 24 lists seems reasonable, and caps the cost of the listsArray to 96bytes(32-bit,24-way) or 192bytes(64-bit,24-way).
+ private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount;
+
+ private volatile ManualResetEvent m_kernelEvent; //lazily initialized if required.
+
+ private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists;
+
+ // legal values for m_state
+ private const int CANNOT_BE_CANCELED = 0;
+ private const int NOT_CANCELED = 1;
+ private const int NOTIFYING = 2;
+ private const int NOTIFYINGCOMPLETE = 3;
+
+ //m_state uses the pattern "volatile int32 reads, with cmpxch writes" which is safe for updates and cannot suffer torn reads.
+ private volatile int m_state;
+
+
+ /// The ID of the thread currently executing the main body of CTS.Cancel()
+ /// this helps us to know if a call to ctr.Dispose() is running 'within' a cancellation callback.
+ /// This is updated as we move between the main thread calling cts.Cancel() and any syncContexts that are used to
+ /// actually run the callbacks.
+ private volatile int m_threadIDExecutingCallbacks = -1;
+
+ private bool m_disposed;
+
+ // we track the running callback to assist ctr.Dispose() to wait for the target callback to complete.
+ private volatile CancellationCallbackInfo m_executingCallback;
+
+ // provided for CancelAfter and timer-related constructors
+ private volatile Timer m_timer;
+
+ // ----------------------
+ // ** public properties
+
+ /// <summary>
+ /// Gets whether cancellation has been requested for this <see
+ /// cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see>.
+ /// </summary>
+ /// <value>Whether cancellation has been requested for this <see
+ /// cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see>.</value>
+ /// <remarks>
+ /// <para>
+ /// This property indicates whether cancellation has been requested for this token source, such as
+ /// due to a call to its
+ /// <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see> method.
+ /// </para>
+ /// <para>
+ /// If this property returns true, it only guarantees that cancellation has been requested. It does not
+ /// guarantee that every handler registered with the corresponding token has finished executing, nor
+ /// that cancellation requests have finished propagating to all registered handlers. Additional
+ /// synchronization may be required, particularly in situations where related objects are being
+ /// canceled concurrently.
+ /// </para>
+ /// </remarks>
+ public bool IsCancellationRequested
+ {
+ get { return m_state >= NOTIFYING; }
+ }
+
+ /// <summary>
+ /// A simple helper to determine whether cancellation has finished.
+ /// </summary>
+ internal bool IsCancellationCompleted
+ {
+ get { return m_state == NOTIFYINGCOMPLETE; }
+ }
+
+ /// <summary>
+ /// A simple helper to determine whether disposal has occurred.
+ /// </summary>
+ internal bool IsDisposed
+ {
+ get { return m_disposed; }
+ }
+
+ /// <summary>
+ /// The ID of the thread that is running callbacks.
+ /// </summary>
+ internal int ThreadIDExecutingCallbacks
+ {
+ set { m_threadIDExecutingCallbacks = value; }
+ get { return m_threadIDExecutingCallbacks; }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// associated with this <see cref="CancellationTokenSource"/>.
+ /// </summary>
+ /// <value>The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// associated with this <see cref="CancellationTokenSource"/>.</value>
+ /// <exception cref="T:System.ObjectDisposedException">The token source has been
+ /// disposed.</exception>
+ public CancellationToken Token
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return new CancellationToken(this);
+ }
+ }
+
+ // ----------------------
+ // ** internal and private properties.
+
+ /// <summary>
+ ///
+ /// </summary>
+ internal bool CanBeCanceled
+ {
+ get { return m_state != CANNOT_BE_CANCELED; }
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ internal WaitHandle WaitHandle
+ {
+ get
+ {
+ ThrowIfDisposed();
+
+ // fast path if already allocated.
+ if (m_kernelEvent != null)
+ return m_kernelEvent;
+
+ // lazy-init the mre.
+ ManualResetEvent mre = new ManualResetEvent(false);
+ if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null)
+ {
+ ((IDisposable)mre).Dispose();
+ }
+
+ // There is a race condition between checking IsCancellationRequested and setting the event.
+ // However, at this point, the kernel object definitely exists and the cases are:
+ // 1. if IsCancellationRequested = true, then we will call Set()
+ // 2. if IsCancellationRequested = false, then NotifyCancellation will see that the event exists, and will call Set().
+ if (IsCancellationRequested)
+ m_kernelEvent.Set();
+
+ return m_kernelEvent;
+ }
+ }
+
+
+ /// <summary>
+ /// The currently executing callback
+ /// </summary>
+ internal CancellationCallbackInfo ExecutingCallback
+ {
+ get { return m_executingCallback; }
+ }
+
+#if DEBUG
+ /// <summary>
+ /// Used by the dev unit tests to check the number of outstanding registrations.
+ /// They use private reflection to gain access. Because this would be dead retail
+ /// code, however, it is ifdef'd out to work only in debug builds.
+ /// </summary>
+ private int CallbackCount
+ {
+ get
+ {
+ SparselyPopulatedArray<CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists;
+ if (callbackLists == null)
+ return 0;
+
+ int count = 0;
+ foreach(SparselyPopulatedArray<CancellationCallbackInfo> sparseArray in callbackLists)
+ {
+ if(sparseArray != null)
+ {
+ SparselyPopulatedArrayFragment<CancellationCallbackInfo> currCallbacks = sparseArray.Head;
+ while (currCallbacks != null)
+ {
+ for (int i = 0; i < currCallbacks.Length; i++)
+ if (currCallbacks[i] != null)
+ count++;
+
+ currCallbacks = currCallbacks.Next;
+ }
+ }
+ }
+ return count;
+ }
+ }
+#endif
+
+ // ** Public Constructors
+
+ /// <summary>
+ /// Initializes the <see cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ public CancellationTokenSource()
+ {
+ m_state = NOT_CANCELED;
+ }
+
+ // ** Private constructors for static sources.
+ // set=false ==> cannot be canceled.
+ // set=true ==> is canceled.
+ private CancellationTokenSource(bool set)
+ {
+ m_state = set ? NOTIFYINGCOMPLETE : CANNOT_BE_CANCELED;
+ }
+
+ /// <summary>
+ /// Constructs a <see cref="T:System.Threading.CancellationTokenSource"/> that will be canceled after a specified time span.
+ /// </summary>
+ /// <param name="delay">The time span to wait before canceling this <see cref="T:System.Threading.CancellationTokenSource"/></param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// The countdown for the delay starts during the call to the constructor. When the delay expires,
+ /// the constructed <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
+ /// not been canceled already.
+ /// </para>
+ /// <para>
+ /// Subsequent calls to CancelAfter will reset the delay for the constructed
+ /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
+ /// canceled already.
+ /// </para>
+ /// </remarks>
+ public CancellationTokenSource(TimeSpan delay)
+ {
+ long totalMilliseconds = (long)delay.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("delay");
+ }
+
+ InitializeWithTimer((int)totalMilliseconds);
+ }
+
+ /// <summary>
+ /// Constructs a <see cref="T:System.Threading.CancellationTokenSource"/> that will be canceled after a specified time span.
+ /// </summary>
+ /// <param name="millisecondsDelay">The time span to wait before canceling this <see cref="T:System.Threading.CancellationTokenSource"/></param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when <paramref name="millisecondsDelay"/> is less than -1.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// The countdown for the millisecondsDelay starts during the call to the constructor. When the millisecondsDelay expires,
+ /// the constructed <see cref="T:System.Threading.CancellationTokenSource"/> is canceled (if it has
+ /// not been canceled already).
+ /// </para>
+ /// <para>
+ /// Subsequent calls to CancelAfter will reset the millisecondsDelay for the constructed
+ /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
+ /// canceled already.
+ /// </para>
+ /// </remarks>
+ public CancellationTokenSource(Int32 millisecondsDelay)
+ {
+ if (millisecondsDelay < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsDelay");
+ }
+
+ InitializeWithTimer(millisecondsDelay);
+ }
+
+ // Common initialization logic when constructing a CTS with a delay parameter
+ private void InitializeWithTimer(Int32 millisecondsDelay)
+ {
+ m_state = NOT_CANCELED;
+ m_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
+ }
+
+ // ** Public Methods
+
+ /// <summary>
+ /// Communicates a request for cancellation.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The associated <see cref="T:System.Threading.CancellationToken" /> will be
+ /// notified of the cancellation and will transition to a state where
+ /// <see cref="System.Threading.CancellationToken.IsCancellationRequested">IsCancellationRequested</see> returns true.
+ /// Any callbacks or cancelable operations
+ /// registered with the <see cref="T:System.Threading.CancellationToken"/> will be executed.
+ /// </para>
+ /// <para>
+ /// Cancelable operations and callbacks registered with the token should not throw exceptions.
+ /// However, this overload of Cancel will aggregate any exceptions thrown into a <see cref="System.AggregateException"/>,
+ /// such that one callback throwing an exception will not prevent other registered callbacks from being executed.
+ /// </para>
+ /// <para>
+ /// The <see cref="T:System.Threading.ExecutionContext"/> that was captured when each callback was registered
+ /// will be reestablished when the callback is invoked.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.AggregateException">An aggregate exception containing all the exceptions thrown
+ /// by the registered callbacks on the associated <see cref="T:System.Threading.CancellationToken"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">This <see
+ /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.</exception>
+ public void Cancel()
+ {
+ Cancel(false);
+ }
+
+ /// <summary>
+ /// Communicates a request for cancellation.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The associated <see cref="T:System.Threading.CancellationToken" /> will be
+ /// notified of the cancellation and will transition to a state where
+ /// <see cref="System.Threading.CancellationToken.IsCancellationRequested">IsCancellationRequested</see> returns true.
+ /// Any callbacks or cancelable operations
+ /// registered with the <see cref="T:System.Threading.CancellationToken"/> will be executed.
+ /// </para>
+ /// <para>
+ /// Cancelable operations and callbacks registered with the token should not throw exceptions.
+ /// If <paramref name="throwOnFirstException"/> is true, an exception will immediately propagate out of the
+ /// call to Cancel, preventing the remaining callbacks and cancelable operations from being processed.
+ /// If <paramref name="throwOnFirstException"/> is false, this overload will aggregate any
+ /// exceptions thrown into a <see cref="System.AggregateException"/>,
+ /// such that one callback throwing an exception will not prevent other registered callbacks from being executed.
+ /// </para>
+ /// <para>
+ /// The <see cref="T:System.Threading.ExecutionContext"/> that was captured when each callback was registered
+ /// will be reestablished when the callback is invoked.
+ /// </para>
+ /// </remarks>
+ /// <param name="throwOnFirstException">Specifies whether exceptions should immediately propagate.</param>
+ /// <exception cref="T:System.AggregateException">An aggregate exception containing all the exceptions thrown
+ /// by the registered callbacks on the associated <see cref="T:System.Threading.CancellationToken"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">This <see
+ /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.</exception>
+ public void Cancel(bool throwOnFirstException)
+ {
+ ThrowIfDisposed();
+ NotifyCancellation(throwOnFirstException);
+ }
+
+ /// <summary>
+ /// Schedules a Cancel operation on this <see cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ /// <param name="delay">The time span to wait before canceling this <see
+ /// cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </param>
+ /// <exception cref="T:System.ObjectDisposedException">The exception thrown when this <see
+ /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception thrown when <paramref name="delay"/> is less than -1 or
+ /// greater than Int32.MaxValue.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// The countdown for the delay starts during this call. When the delay expires,
+ /// this <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
+ /// not been canceled already.
+ /// </para>
+ /// <para>
+ /// Subsequent calls to CancelAfter will reset the delay for this
+ /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
+ /// canceled already.
+ /// </para>
+ /// </remarks>
+ public void CancelAfter(TimeSpan delay)
+ {
+ long totalMilliseconds = (long)delay.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("delay");
+ }
+
+ CancelAfter((int)totalMilliseconds);
+ }
+
+ /// <summary>
+ /// Schedules a Cancel operation on this <see cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ /// <param name="millisecondsDelay">The time span to wait before canceling this <see
+ /// cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </param>
+ /// <exception cref="T:System.ObjectDisposedException">The exception thrown when this <see
+ /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception thrown when <paramref name="millisecondsDelay"/> is less than -1.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// The countdown for the millisecondsDelay starts during this call. When the millisecondsDelay expires,
+ /// this <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
+ /// not been canceled already.
+ /// </para>
+ /// <para>
+ /// Subsequent calls to CancelAfter will reset the millisecondsDelay for this
+ /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
+ /// canceled already.
+ /// </para>
+ /// </remarks>
+ public void CancelAfter(Int32 millisecondsDelay)
+ {
+ ThrowIfDisposed();
+
+ if (millisecondsDelay < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsDelay");
+ }
+
+ if (IsCancellationRequested) return;
+
+ // There is a race condition here as a Cancel could occur between the check of
+ // IsCancellationRequested and the creation of the timer. This is benign; in the
+ // worst case, a timer will be created that has no effect when it expires.
+
+ // Also, if Dispose() is called right here (after ThrowIfDisposed(), before timer
+ // creation), it would result in a leaked Timer object (at least until the timer
+ // expired and Disposed itself). But this would be considered bad behavior, as
+ // Dispose() is not thread-safe and should not be called concurrently with CancelAfter().
+
+ if (m_timer == null)
+ {
+ // Lazily initialize the timer in a thread-safe fashion.
+ // Initially set to "never go off" because we don't want to take a
+ // chance on a timer "losing" the initialization and then
+ // cancelling the token before it (the timer) can be disposed.
+ Timer newTimer = new Timer(s_timerCallback, this, -1, -1);
+ if (Interlocked.CompareExchange(ref m_timer, newTimer, null) != null)
+ {
+ // We did not initialize the timer. Dispose the new timer.
+ newTimer.Dispose();
+ }
+ }
+
+
+ // It is possible that m_timer has already been disposed, so we must do
+ // the following in a try/catch block.
+ try
+ {
+ m_timer.Change(millisecondsDelay, -1);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Just eat the exception. There is no other way to tell that
+ // the timer has been disposed, and even if there were, there
+ // would not be a good way to deal with the observe/dispose
+ // race condition.
+ }
+
+ }
+
+ private static readonly TimerCallback s_timerCallback = new TimerCallback(TimerCallbackLogic);
+
+ // Common logic for a timer delegate
+ private static void TimerCallbackLogic(object obj)
+ {
+ CancellationTokenSource cts = (CancellationTokenSource)obj;
+
+ // Cancel the source; handle a race condition with cts.Dispose()
+ if (!cts.IsDisposed)
+ {
+ // There is a small window for a race condition where a cts.Dispose can sneak
+ // in right here. I'll wrap the cts.Cancel() in a try/catch to proof us
+ // against this race condition.
+ try
+ {
+ cts.Cancel(); // will take care of disposing of m_timer
+ }
+ catch (ObjectDisposedException)
+ {
+ // If the ODE was not due to the target cts being disposed, then propagate the ODE.
+ if (!cts.IsDisposed) throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.CancellationTokenSource" />.
+ /// </summary>
+ /// <remarks>
+ /// This method is not thread-safe for any other concurrent calls.
+ /// </remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases the unmanaged resources used by the <see cref="T:System.Threading.CancellationTokenSource" /> class and optionally releases the managed resources.
+ /// </summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ // There is nothing to do if disposing=false because the CancellationTokenSource holds no unmanaged resources.
+
+ if (disposing && !m_disposed)
+ {
+ //NOTE: We specifically tolerate that a callback can be deregistered
+ // after the CTS has been disposed and/or concurrently with cts.Dispose().
+ // This is safe without locks because the reg.Dispose() only
+ // mutates a sparseArrayFragment and then reads from properties of the CTS that are not
+ // invalidated by cts.Dispose().
+ //
+ // We also tolerate that a callback can be registered after the CTS has been
+ // disposed. This is safe without locks because InternalRegister is tolerant
+ // of m_registeredCallbacksLists becoming null during its execution. However,
+ // we run the acceptable risk of m_registeredCallbacksLists getting reinitialized
+ // to non-null if there is a race between Dispose and Register, in which case this
+ // instance may unnecessarily hold onto a registered callback. But that's no worse
+ // than if Dispose wasn't safe to use concurrently, as Dispose would never be called,
+ // and thus no handlers would be dropped.
+ //
+ // And, we tolerate Dispose being used concurrently with Cancel. This is necessary
+ // to properly support LinkedCancellationTokenSource, where, due to common usage patterns,
+ // it's possible for this pairing to occur with valid usage (e.g. a component accepts
+ // an external CancellationToken and uses CreateLinkedTokenSource to combine it with an
+ // internal source of cancellation, then Disposes of that linked source, which could
+ // happen at the same time the external entity is requesting cancellation).
+
+ m_timer?.Dispose(); // Timer.Dispose is thread-safe
+
+ // registered callbacks are now either complete or will never run, due to guarantees made by ctr.Dispose()
+ // so we can now perform main disposal work without risk of linking callbacks trying to use this CTS.
+
+ m_registeredCallbacksLists = null; // free for GC; Cancel correctly handles a null field
+
+ // If a kernel event was created via WaitHandle, we'd like to Dispose of it. However,
+ // we only want to do so if it's not being used by Cancel concurrently. First, we
+ // interlocked exchange it to be null, and then we check whether cancellation is currently
+ // in progress. NotifyCancellation will only try to set the event if it exists after it's
+ // transitioned to and while it's in the NOTIFYING state.
+ if (m_kernelEvent != null)
+ {
+ ManualResetEvent mre = Interlocked.Exchange(ref m_kernelEvent, null);
+ if (mre != null && m_state != NOTIFYING)
+ {
+ mre.Dispose();
+ }
+ }
+
+ m_disposed = true;
+ }
+ }
+
+ // -- Internal methods.
+
+ /// <summary>
+ /// Throws an exception if the source has been disposed.
+ /// </summary>
+ internal void ThrowIfDisposed()
+ {
+ if (m_disposed)
+ ThrowObjectDisposedException();
+ }
+
+ // separation enables inlining of ThrowIfDisposed
+ private static void ThrowObjectDisposedException()
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationTokenSource_Disposed"));
+ }
+
+ /// <summary>
+ /// InternalGetStaticSource()
+ /// </summary>
+ /// <param name="set">Whether the source should be set.</param>
+ /// <returns>A static source to be shared among multiple tokens.</returns>
+ internal static CancellationTokenSource InternalGetStaticSource(bool set)
+ {
+ return set ? _staticSource_Set : _staticSource_NotCancelable;
+ }
+
+ /// <summary>
+ /// Registers a callback object. If cancellation has already occurred, the
+ /// callback will have been run by the time this method returns.
+ /// </summary>
+ internal CancellationTokenRegistration InternalRegister(
+ Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext executionContext)
+ {
+ if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ {
+ ThrowIfDisposed();
+ }
+
+ // the CancellationToken has already checked that the token is cancelable before calling this method.
+ Contract.Assert(CanBeCanceled, "Cannot register for uncancelable token src");
+
+ // if not canceled, register the event handlers
+ // if canceled already, run the callback synchronously
+ // Apart from the semantics of late-enlistment, this also ensures that during ExecuteCallbackHandlers() there
+ // will be no mutation of the _registeredCallbacks list
+
+ if (!IsCancellationRequested)
+ {
+ // In order to enable code to not leak too many handlers, we allow Dispose to be called concurrently
+ // with Register. While this is not a recommended practice, consumers can and do use it this way.
+ // We don't make any guarantees about whether the CTS will hold onto the supplied callback
+ // if the CTS has already been disposed when the callback is registered, but we try not to
+ // while at the same time not paying any non-negligible overhead. The simple compromise
+ // is to check whether we're disposed (not volatile), and if we see we are, to return an empty
+ // registration, just as if CanBeCanceled was false for the check made in CancellationToken.Register.
+ // If there's a race and m_disposed is false even though it's been disposed, or if the disposal request
+ // comes in after this line, we simply run the minor risk of having m_registeredCallbacksLists reinitialized
+ // (after it was cleared to null during Dispose).
+
+ if (m_disposed && !AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ return new CancellationTokenRegistration();
+
+ int myIndex = Thread.CurrentThread.ManagedThreadId % s_nLists;
+
+ CancellationCallbackInfo callbackInfo = targetSyncContext != null ?
+ new CancellationCallbackInfo.WithSyncContext(callback, stateForCallback, executionContext, this, targetSyncContext) :
+ new CancellationCallbackInfo(callback, stateForCallback, executionContext, this);
+
+ //allocate the callback list array
+ var registeredCallbacksLists = m_registeredCallbacksLists;
+ if (registeredCallbacksLists == null)
+ {
+ SparselyPopulatedArray<CancellationCallbackInfo>[] list = new SparselyPopulatedArray<CancellationCallbackInfo>[s_nLists];
+ registeredCallbacksLists = Interlocked.CompareExchange(ref m_registeredCallbacksLists, list, null);
+ if (registeredCallbacksLists == null) registeredCallbacksLists = list;
+ }
+
+ //allocate the actual lists on-demand to save mem in low-use situations, and to avoid false-sharing.
+ var callbacks = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref registeredCallbacksLists[myIndex]);
+ if (callbacks == null)
+ {
+ SparselyPopulatedArray<CancellationCallbackInfo> callBackArray = new SparselyPopulatedArray<CancellationCallbackInfo>(4);
+ Interlocked.CompareExchange(ref (registeredCallbacksLists[myIndex]), callBackArray, null);
+ callbacks = registeredCallbacksLists[myIndex];
+ }
+
+ // Now add the registration to the list.
+ SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> addInfo = callbacks.Add(callbackInfo);
+ CancellationTokenRegistration registration = new CancellationTokenRegistration(callbackInfo, addInfo);
+
+ if (!IsCancellationRequested)
+ return registration;
+
+ // If a cancellation has since come in, we will try to undo the registration and run the callback ourselves.
+ // (this avoids leaving the callback orphaned)
+ bool deregisterOccurred = registration.TryDeregister();
+
+ if (!deregisterOccurred)
+ {
+ // The thread that is running Cancel() snagged our callback for execution.
+ // So we don't need to run it, but we do return the registration so that
+ // ctr.Dispose() will wait for callback completion.
+ return registration;
+ }
+ }
+
+ // If cancellation already occurred, we run the callback on this thread and return an empty registration.
+ callback(stateForCallback);
+ return new CancellationTokenRegistration();
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ private void NotifyCancellation(bool throwOnFirstException)
+ {
+ // fast-path test to check if Notify has been called previously
+ if (IsCancellationRequested)
+ return;
+
+ // If we're the first to signal cancellation, do the main extra work.
+ if (Interlocked.CompareExchange(ref m_state, NOTIFYING, NOT_CANCELED) == NOT_CANCELED)
+ {
+ // Dispose of the timer, if any. Dispose may be running concurrently here, but Timer.Dispose is thread-safe.
+ m_timer?.Dispose();
+
+ // Record the threadID being used for running the callbacks.
+ ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
+
+ // Set the event if it's been lazily initialized and hasn't yet been disposed of. Dispose may
+ // be running concurrently, in which case either it'll have set m_kernelEvent back to null and
+ // we won't see it here, or it'll see that we've transitioned to NOTIFYING and will skip disposing it,
+ // leaving cleanup to finalization.
+ m_kernelEvent?.Set(); // update the MRE value.
+
+ // - late enlisters to the Canceled event will have their callbacks called immediately in the Register() methods.
+ // - Callbacks are not called inside a lock.
+ // - After transition, no more delegates will be added to the
+ // - list of handlers, and hence it can be consumed and cleared at leisure by ExecuteCallbackHandlers.
+ ExecuteCallbackHandlers(throwOnFirstException);
+ Contract.Assert(IsCancellationCompleted, "Expected cancellation to have finished");
+ }
+ }
+
+ /// <summary>
+ /// Invoke the Canceled event.
+ /// </summary>
+ /// <remarks>
+ /// The handlers are invoked synchronously in LIFO order.
+ /// </remarks>
+ private void ExecuteCallbackHandlers(bool throwOnFirstException)
+ {
+ Contract.Assert(IsCancellationRequested, "ExecuteCallbackHandlers should only be called after setting IsCancellationRequested->true");
+ Contract.Assert(ThreadIDExecutingCallbacks != -1, "ThreadIDExecutingCallbacks should have been set.");
+
+ // Design decision: call the delegates in LIFO order so that callbacks fire 'deepest first'.
+ // This is intended to help with nesting scenarios so that child enlisters cancel before their parents.
+ List<Exception> exceptionList = null;
+ SparselyPopulatedArray<CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists;
+
+ // If there are no callbacks to run, we can safely exit. Any race conditions to lazy initialize it
+ // will see IsCancellationRequested and will then run the callback themselves.
+ if (callbackLists == null)
+ {
+ Interlocked.Exchange(ref m_state, NOTIFYINGCOMPLETE);
+ return;
+ }
+
+ try
+ {
+ for (int index = 0; index < callbackLists.Length; index++)
+ {
+ SparselyPopulatedArray<CancellationCallbackInfo> list = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref callbackLists[index]);
+ if (list != null)
+ {
+ SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment = list.Tail;
+
+ while (currArrayFragment != null)
+ {
+ for (int i = currArrayFragment.Length - 1; i >= 0; i--)
+ {
+ // 1a. publish the indended callback, to ensure ctr.Dipose can tell if a wait is necessary.
+ // 1b. transition to the target syncContext and continue there..
+ // On the target SyncContext.
+ // 2. actually remove the callback
+ // 3. execute the callback
+ // re:#2 we do the remove on the syncCtx so that we can be sure we have control of the syncCtx before
+ // grabbing the callback. This prevents a deadlock if ctr.Dispose() might run on the syncCtx too.
+ m_executingCallback = currArrayFragment[i];
+ if (m_executingCallback != null)
+ {
+ //Transition to the target sync context (if necessary), and continue our work there.
+ CancellationCallbackCoreWorkArguments args = new CancellationCallbackCoreWorkArguments(currArrayFragment, i);
+
+ // marshal exceptions: either aggregate or perform an immediate rethrow
+ // We assume that syncCtx.Send() has forwarded on user exceptions when appropriate.
+ try
+ {
+ var wsc = m_executingCallback as CancellationCallbackInfo.WithSyncContext;
+ if (wsc != null)
+ {
+ Contract.Assert(wsc.TargetSyncContext != null, "Should only have derived CCI if non-null SyncCtx");
+ wsc.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, args);
+ // CancellationCallbackCoreWork_OnSyncContext may have altered ThreadIDExecutingCallbacks, so reset it.
+ ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
+ }
+ else
+ {
+ CancellationCallbackCoreWork(args);
+ }
+ }
+ catch(Exception ex)
+ {
+ if (throwOnFirstException)
+ throw;
+
+ // Otherwise, log it and proceed.
+ if(exceptionList == null)
+ exceptionList = new List<Exception>();
+ exceptionList.Add(ex);
+ }
+ }
+ }
+
+ currArrayFragment = currArrayFragment.Prev;
+ }
+ }
+ }
+ }
+ finally
+ {
+ m_state = NOTIFYINGCOMPLETE;
+ m_executingCallback = null;
+ Thread.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state.
+ }
+
+ if (exceptionList != null)
+ {
+ Contract.Assert(exceptionList.Count > 0, "Expected exception count > 0");
+ throw new AggregateException(exceptionList);
+ }
+ }
+
+ // The main callback work that executes on the target synchronization context
+ private void CancellationCallbackCoreWork_OnSyncContext(object obj)
+ {
+ CancellationCallbackCoreWork((CancellationCallbackCoreWorkArguments)obj);
+ }
+
+ private void CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args)
+ {
+ // remove the intended callback..and ensure that it worked.
+ // otherwise the callback has disappeared in the interim and we can immediately return.
+ CancellationCallbackInfo callback = args.m_currArrayFragment.SafeAtomicRemove(args.m_currArrayIndex, m_executingCallback);
+ if (callback == m_executingCallback)
+ {
+ if (callback.TargetExecutionContext != null)
+ {
+ // we are running via a custom sync context, so update the executing threadID
+ callback.CancellationTokenSource.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
+ }
+ callback.ExecuteCallback();
+ }
+ }
+
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that will be in the canceled state
+ /// when any of the source tokens are in the canceled state.
+ /// </summary>
+ /// <param name="token1">The first <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to observe.</param>
+ /// <param name="token2">The second <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to observe.</param>
+ /// <returns>A <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that is linked
+ /// to the source tokens.</returns>
+ public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2)
+ {
+ return token1.CanBeCanceled || token2.CanBeCanceled ?
+ new LinkedCancellationTokenSource(token1, token2) :
+ new CancellationTokenSource();
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that will be in the canceled state
+ /// when any of the source tokens are in the canceled state.
+ /// </summary>
+ /// <param name="tokens">The <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances to observe.</param>
+ /// <returns>A <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that is linked
+ /// to the source tokens.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="tokens"/> is null.</exception>
+ public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens)
+ {
+ if (tokens == null)
+ throw new ArgumentNullException("tokens");
+
+ if (tokens.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty"));
+
+ // a defensive copy is not required as the array has value-items that have only a single IntPtr field,
+ // hence each item cannot be null itself, and reads of the payloads cannot be torn.
+ Contract.EndContractBlock();
+
+ return new LinkedCancellationTokenSource(tokens);
+ }
+
+
+ // Wait for a single callback to complete (or, more specifically, to not be running).
+ // It is ok to call this method if the callback has already finished.
+ // Calling this method before the target callback has been selected for execution would be an error.
+ internal void WaitForCallbackToComplete(CancellationCallbackInfo callbackInfo)
+ {
+ SpinWait sw = new SpinWait();
+ while (ExecutingCallback == callbackInfo)
+ {
+ sw.SpinOnce(); //spin as we assume callback execution is fast and that this situation is rare.
+ }
+ }
+
+ private sealed class LinkedCancellationTokenSource : CancellationTokenSource
+ {
+ private static readonly Action<object> s_linkedTokenCancelDelegate =
+ s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel()
+ private CancellationTokenRegistration[] m_linkingRegistrations;
+
+ internal LinkedCancellationTokenSource(CancellationToken token1, CancellationToken token2)
+ {
+ bool token2CanBeCanceled = token2.CanBeCanceled;
+
+ if (token1.CanBeCanceled)
+ {
+ m_linkingRegistrations = new CancellationTokenRegistration[token2CanBeCanceled ? 2 : 1]; // there will be at least 1 and at most 2 linkings
+ m_linkingRegistrations[0] = token1.InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this);
+ }
+
+ if (token2CanBeCanceled)
+ {
+ int index = 1;
+ if (m_linkingRegistrations == null)
+ {
+ m_linkingRegistrations = new CancellationTokenRegistration[1]; // this will be the only linking
+ index = 0;
+ }
+ m_linkingRegistrations[index] = token2.InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this);
+ }
+ }
+
+ internal LinkedCancellationTokenSource(params CancellationToken[] tokens)
+ {
+ m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length];
+
+ for (int i = 0; i < tokens.Length; i++)
+ {
+ if (tokens[i].CanBeCanceled)
+ {
+ m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this);
+ }
+ // Empty slots in the array will be default(CancellationTokenRegistration), which are nops to Dispose.
+ // Based on usage patterns, such occurrences should also be rare, such that it's not worth resizing
+ // the array and incurring the related costs.
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposing || m_disposed)
+ return;
+
+ CancellationTokenRegistration[] linkingRegistrations = m_linkingRegistrations;
+ if (linkingRegistrations != null)
+ {
+ m_linkingRegistrations = null; // release for GC once we're done enumerating
+ for (int i = 0; i < linkingRegistrations.Length; i++)
+ {
+ linkingRegistrations[i].Dispose();
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ }
+ }
+
+ // ----------------------------------------------------------
+ // -- CancellationCallbackCoreWorkArguments --
+ // ----------------------------------------------------------
+ // Helper struct for passing data to the target sync context
+ internal struct CancellationCallbackCoreWorkArguments
+ {
+ internal SparselyPopulatedArrayFragment<CancellationCallbackInfo> m_currArrayFragment;
+ internal int m_currArrayIndex;
+
+ public CancellationCallbackCoreWorkArguments(SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment, int currArrayIndex)
+ {
+ m_currArrayFragment = currArrayFragment;
+ m_currArrayIndex = currArrayIndex;
+ }
+ }
+
+ // ----------------------------------------------------------
+ // -- CancellationCallbackInfo --
+ // ----------------------------------------------------------
+
+ /// <summary>
+ /// A helper class for collating the various bits of information required to execute
+ /// cancellation callbacks.
+ /// </summary>
+ internal class CancellationCallbackInfo
+ {
+ internal readonly Action<object> Callback;
+ internal readonly object StateForCallback;
+ internal readonly ExecutionContext TargetExecutionContext;
+ internal readonly CancellationTokenSource CancellationTokenSource;
+
+ internal sealed class WithSyncContext : CancellationCallbackInfo
+ {
+ // Very rarely used, and as such it is separated out into a
+ // a derived type so that the space for it is pay-for-play.
+ internal readonly SynchronizationContext TargetSyncContext;
+
+ internal WithSyncContext(
+ Action<object> callback, object stateForCallback, ExecutionContext targetExecutionContext, CancellationTokenSource cancellationTokenSource,
+ SynchronizationContext targetSyncContext) :
+ base(callback, stateForCallback, targetExecutionContext, cancellationTokenSource)
+ {
+ TargetSyncContext = targetSyncContext;
+ }
+
+ }
+
+ internal CancellationCallbackInfo(
+ Action<object> callback, object stateForCallback, ExecutionContext targetExecutionContext, CancellationTokenSource cancellationTokenSource)
+ {
+ Callback = callback;
+ StateForCallback = stateForCallback;
+ TargetExecutionContext = targetExecutionContext;
+ CancellationTokenSource = cancellationTokenSource;
+ }
+
+ // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical
+ [SecurityCritical]
+ private static ContextCallback s_executionContextCallback;
+
+ /// <summary>
+ /// InternalExecuteCallbackSynchronously_GeneralPath
+ /// This will be called on the target synchronization context, however, we still need to restore the required execution context
+ /// </summary>
+ [SecuritySafeCritical]
+ internal void ExecuteCallback()
+ {
+ if (TargetExecutionContext != null)
+ {
+ // Lazily initialize the callback delegate; benign race condition
+ var callback = s_executionContextCallback;
+ if (callback == null) s_executionContextCallback = callback = new ContextCallback(ExecutionContextCallback);
+
+ ExecutionContext.Run(
+ TargetExecutionContext,
+ callback,
+ this);
+ }
+ else
+ {
+ //otherwise run directly
+ ExecutionContextCallback(this);
+ }
+ }
+
+ // the worker method to actually run the callback
+ // The signature is such that it can be used as a 'ContextCallback'
+ [SecurityCritical]
+ private static void ExecutionContextCallback(object obj)
+ {
+ CancellationCallbackInfo callbackInfo = obj as CancellationCallbackInfo;
+ Contract.Assert(callbackInfo != null);
+ callbackInfo.Callback(callbackInfo.StateForCallback);
+ }
+ }
+
+
+ // ----------------------------------------------------------
+ // -- SparselyPopulatedArray --
+ // ----------------------------------------------------------
+
+ /// <summary>
+ /// A sparsely populated array. Elements can be sparse and some null, but this allows for
+ /// lock-free additions and growth, and also for constant time removal (by nulling out).
+ /// </summary>
+ /// <typeparam name="T">The kind of elements contained within.</typeparam>
+ internal class SparselyPopulatedArray<T> where T : class
+ {
+ private readonly SparselyPopulatedArrayFragment<T> m_head;
+ private volatile SparselyPopulatedArrayFragment<T> m_tail;
+
+ /// <summary>
+ /// Allocates a new array with the given initial size.
+ /// </summary>
+ /// <param name="initialSize">How many array slots to pre-allocate.</param>
+ internal SparselyPopulatedArray(int initialSize)
+ {
+ m_head = m_tail = new SparselyPopulatedArrayFragment<T>(initialSize);
+ }
+
+#if DEBUG
+ // Used in DEBUG mode by CancellationTokenSource.CallbackCount
+ /// <summary>
+ /// The head of the doubly linked list.
+ /// </summary>
+ internal SparselyPopulatedArrayFragment<T> Head
+ {
+ get { return m_head; }
+ }
+#endif
+
+ /// <summary>
+ /// The tail of the doubly linked list.
+ /// </summary>
+ internal SparselyPopulatedArrayFragment<T> Tail
+ {
+ get { return m_tail; }
+ }
+
+ /// <summary>
+ /// Adds an element in the first available slot, beginning the search from the tail-to-head.
+ /// If no slots are available, the array is grown. The method doesn't return until successful.
+ /// </summary>
+ /// <param name="element">The element to add.</param>
+ /// <returns>Information about where the add happened, to enable O(1) deregistration.</returns>
+ internal SparselyPopulatedArrayAddInfo<T> Add(T element)
+ {
+ while (true)
+ {
+ // Get the tail, and ensure it's up to date.
+ SparselyPopulatedArrayFragment<T> tail = m_tail;
+ while (tail.m_next != null)
+ m_tail = (tail = tail.m_next);
+
+ // Search for a free index, starting from the tail.
+ SparselyPopulatedArrayFragment<T> curr = tail;
+ while (curr != null)
+ {
+ const int RE_SEARCH_THRESHOLD = -10; // Every 10 skips, force a search.
+ if (curr.m_freeCount < 1)
+ --curr.m_freeCount;
+
+ if (curr.m_freeCount > 0 || curr.m_freeCount < RE_SEARCH_THRESHOLD)
+ {
+ int c = curr.Length;
+
+ // We'll compute a start offset based on how many free slots we think there
+ // are. This optimizes for ordinary the LIFO deregistration pattern, and is
+ // far from perfect due to the non-threadsafe ++ and -- of the free counter.
+ int start = ((c - curr.m_freeCount) % c);
+ if (start < 0)
+ {
+ start = 0;
+ curr.m_freeCount--; // Too many free elements; fix up.
+ }
+ Contract.Assert(start >= 0 && start < c, "start is outside of bounds");
+
+ // Now walk the array until we find a free slot (or reach the end).
+ for (int i = 0; i < c; i++)
+ {
+ // If the slot is null, try to CAS our element into it.
+ int tryIndex = (start + i) % c;
+ Contract.Assert(tryIndex >= 0 && tryIndex < curr.m_elements.Length, "tryIndex is outside of bounds");
+
+ if (curr.m_elements[tryIndex] == null && Interlocked.CompareExchange(ref curr.m_elements[tryIndex], element, null) == null)
+ {
+ // We adjust the free count by --. Note: if this drops to 0, we will skip
+ // the fragment on the next search iteration. Searching threads will -- the
+ // count and force a search every so often, just in case fragmentation occurs.
+ int newFreeCount = curr.m_freeCount - 1;
+ curr.m_freeCount = newFreeCount > 0 ? newFreeCount : 0;
+ return new SparselyPopulatedArrayAddInfo<T>(curr, tryIndex);
+ }
+ }
+ }
+
+ curr = curr.m_prev;
+ }
+
+ // If we got here, we need to add a new chunk to the tail and try again.
+ SparselyPopulatedArrayFragment<T> newTail = new SparselyPopulatedArrayFragment<T>(
+ tail.m_elements.Length == 4096 ? 4096 : tail.m_elements.Length * 2, tail);
+ if (Interlocked.CompareExchange(ref tail.m_next, newTail, null) == null)
+ {
+ m_tail = newTail;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// A struct to hold a link to the exact spot in an array an element was inserted, enabling
+ /// constant time removal later on.
+ /// </summary>
+ internal struct SparselyPopulatedArrayAddInfo<T> where T : class
+ {
+ private SparselyPopulatedArrayFragment<T> m_source;
+ private int m_index;
+
+ internal SparselyPopulatedArrayAddInfo(SparselyPopulatedArrayFragment<T> source, int index)
+ {
+ Contract.Assert(source != null);
+ Contract.Assert(index >= 0 && index < source.Length);
+ m_source = source;
+ m_index = index;
+ }
+
+ internal SparselyPopulatedArrayFragment<T> Source
+ {
+ get { return m_source; }
+ }
+
+ internal int Index
+ {
+ get { return m_index; }
+ }
+ }
+
+ /// <summary>
+ /// A fragment of a sparsely populated array, doubly linked.
+ /// </summary>
+ /// <typeparam name="T">The kind of elements contained within.</typeparam>
+ internal class SparselyPopulatedArrayFragment<T> where T : class
+ {
+ internal readonly T[] m_elements; // The contents, sparsely populated (with nulls).
+ internal volatile int m_freeCount; // A hint of the number of free elements.
+ internal volatile SparselyPopulatedArrayFragment<T> m_next; // The next fragment in the chain.
+ internal volatile SparselyPopulatedArrayFragment<T> m_prev; // The previous fragment in the chain.
+
+ internal SparselyPopulatedArrayFragment(int size) : this(size, null)
+ {
+ }
+
+ internal SparselyPopulatedArrayFragment(int size, SparselyPopulatedArrayFragment<T> prev)
+ {
+ m_elements = new T[size];
+ m_freeCount = size;
+ m_prev = prev;
+ }
+
+ internal T this[int index]
+ {
+ get { return Volatile.Read<T>(ref m_elements[index]); }
+ }
+
+ internal int Length
+ {
+ get { return m_elements.Length; }
+ }
+
+#if DEBUG
+ // Used in DEBUG mode by CancellationTokenSource.CallbackCount
+ internal SparselyPopulatedArrayFragment<T> Next
+ {
+ get { return m_next; }
+ }
+#endif
+ internal SparselyPopulatedArrayFragment<T> Prev
+ {
+ get { return m_prev; }
+ }
+
+ // only removes the item at the specified index if it is still the expected one.
+ // Returns the prevailing value.
+ // The remove occurred successfully if the return value == expected element
+ // otherwise the remove did not occur.
+ internal T SafeAtomicRemove(int index, T expectedElement)
+ {
+ T prevailingValue = Interlocked.CompareExchange(ref m_elements[index], null, expectedElement);
+ if (prevailingValue != null)
+ ++m_freeCount;
+ return prevailingValue;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/CountdownEvent.cs b/src/mscorlib/src/System/Threading/CountdownEvent.cs
new file mode 100644
index 0000000000..1374766863
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/CountdownEvent.cs
@@ -0,0 +1,592 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A simple coordination data structure that we use for fork/join style parallelism.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics;
+using System.Security.Permissions;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading
+{
+
+ /// <summary>
+ /// Represents a synchronization primitive that is signaled when its count reaches zero.
+ /// </summary>
+ /// <remarks>
+ /// All public and protected members of <see cref="CountdownEvent"/> are thread-safe and may be used
+ /// concurrently from multiple threads, with the exception of Dispose, which
+ /// must only be used when all other operations on the <see cref="CountdownEvent"/> have
+ /// completed, and Reset, which should only be used when no other threads are
+ /// accessing the event.
+ /// </remarks>
+ [ComVisible(false)]
+ [DebuggerDisplay("Initial Count={InitialCount}, Current Count={CurrentCount}")]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class CountdownEvent : IDisposable
+ {
+ // CountdownEvent is a simple synchronization primitive used for fork/join parallelism. We create a
+ // latch with a count of N; threads then signal the latch, which decrements N by 1; other threads can
+ // wait on the latch at any point; when the latch count reaches 0, all threads are woken and
+ // subsequent waiters return without waiting. The implementation internally lazily creates a true
+ // Win32 event as needed. We also use some amount of spinning on MP machines before falling back to a
+ // wait.
+
+ private int m_initialCount; // The original # of signals the latch was instantiated with.
+ private volatile int m_currentCount; // The # of outstanding signals before the latch transitions to a signaled state.
+ private ManualResetEventSlim m_event; // An event used to manage blocking and signaling.
+ private volatile bool m_disposed; // Whether the latch has been disposed.
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="T:System.Threading.CountdownEvent"/> class with the
+ /// specified count.
+ /// </summary>
+ /// <param name="initialCount">The number of signals required to set the <see
+ /// cref="T:System.Threading.CountdownEvent"/>.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/> is less
+ /// than 0.</exception>
+ public CountdownEvent(int initialCount)
+ {
+ if (initialCount < 0)
+ {
+ throw new ArgumentOutOfRangeException("initialCount");
+ }
+
+ m_initialCount = initialCount;
+ m_currentCount = initialCount;
+
+ // Allocate a thin event, which internally defers creation of an actual Win32 event.
+ m_event = new ManualResetEventSlim();
+
+ // If the latch was created with a count of 0, then it's already in the signaled state.
+ if (initialCount == 0)
+ {
+ m_event.Set();
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of remaining signals required to set the event.
+ /// </summary>
+ /// <value>
+ /// The number of remaining signals required to set the event.
+ /// </value>
+ public int CurrentCount
+ {
+ get
+ {
+ int observedCount = m_currentCount;
+ return observedCount < 0 ? 0 : observedCount;
+ }
+ }
+
+ /// <summary>
+ /// Gets the numbers of signals initially required to set the event.
+ /// </summary>
+ /// <value>
+ /// The number of signals initially required to set the event.
+ /// </value>
+ public int InitialCount
+ {
+ get { return m_initialCount; }
+ }
+
+ /// <summary>
+ /// Determines whether the event is set.
+ /// </summary>
+ /// <value>true if the event is set; otherwise, false.</value>
+ public bool IsSet
+ {
+ get
+ {
+ // The latch is "completed" if its current count has reached 0. Note that this is NOT
+ // the same thing is checking the event's IsCompleted property. There is a tiny window
+ // of time, after the final decrement of the current count to 0 and before setting the
+ // event, where the two values are out of sync.
+ return (m_currentCount <= 0);
+ }
+ }
+
+ /// <summary>
+ /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set.
+ /// </summary>
+ /// <value>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set.</value>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
+ /// <remarks>
+ /// <see cref="WaitHandle"/> should only be used if it's needed for integration with code bases
+ /// that rely on having a WaitHandle. If all that's needed is to wait for the <see cref="CountdownEvent"/>
+ /// to be set, the <see cref="Wait()"/> method should be preferred.
+ /// </remarks>
+ public WaitHandle WaitHandle
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return m_event.WaitHandle;
+ }
+ }
+
+ /// <summary>
+ /// Releases all resources used by the current instance of <see cref="T:System.Threading.CountdownEvent"/>.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ public void Dispose()
+ {
+ // Gets rid of this latch's associated resources. This can consist of a Win32 event
+ // which is (lazily) allocated by the underlying thin event. This method is not safe to
+ // call concurrently -- i.e. a caller must coordinate to ensure only one thread is using
+ // the latch at the time of the call to Dispose.
+
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, releases the unmanaged resources used by the
+ /// <see cref="T:System.Threading.CountdownEvent"/>, and optionally releases the managed resources.
+ /// </summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release
+ /// only unmanaged resources.</param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ m_event.Dispose();
+ m_disposed = true;
+ }
+ }
+
+ /// <summary>
+ /// Registers a signal with the <see cref="T:System.Threading.CountdownEvent"/>, decrementing its
+ /// count.
+ /// </summary>
+ /// <returns>true if the signal caused the count to reach zero and the event was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.InvalidOperationException">The current instance is already set.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool Signal()
+ {
+ ThrowIfDisposed();
+ Contract.Assert(m_event != null);
+
+ if (m_currentCount <= 0)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero"));
+ }
+#pragma warning disable 0420
+ int newCount = Interlocked.Decrement(ref m_currentCount);
+#pragma warning restore 0420
+ if (newCount == 0)
+ {
+ m_event.Set();
+ return true;
+ }
+ else if (newCount < 0)
+ {
+ //if the count is decremented below zero, then throw, it's OK to keep the count negative, and we shouldn't set the event here
+ //because there was a thread already which decremented it to zero and set the event
+ throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero"));
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Registers multiple signals with the <see cref="T:System.Threading.CountdownEvent"/>,
+ /// decrementing its count by the specified amount.
+ /// </summary>
+ /// <param name="signalCount">The number of signals to register.</param>
+ /// <returns>true if the signals caused the count to reach zero and the event was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The current instance is already set. -or- Or <paramref name="signalCount"/> is greater than <see
+ /// cref="CurrentCount"/>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less
+ /// than 1.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool Signal(int signalCount)
+ {
+ if (signalCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("signalCount");
+ }
+
+ ThrowIfDisposed();
+ Contract.Assert(m_event != null);
+
+ int observedCount;
+ SpinWait spin = new SpinWait();
+ while (true)
+ {
+ observedCount = m_currentCount;
+
+ // If the latch is already signaled, we will fail.
+ if (observedCount < signalCount)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero"));
+ }
+
+ // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning
+ // for this statement. This warning is clearly senseless for Interlocked operations.
+#pragma warning disable 0420
+ if (Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount)
+#pragma warning restore 0420
+ {
+ break;
+ }
+
+ // The CAS failed. Spin briefly and try again.
+ spin.SpinOnce();
+ }
+
+ // If we were the last to signal, set the event.
+ if (observedCount == signalCount)
+ {
+ m_event.Set();
+ return true;
+ }
+
+ Contract.Assert(m_currentCount >= 0, "latch was decremented below zero");
+ return false;
+ }
+
+ /// <summary>
+ /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">The current instance is already
+ /// set.</exception>
+ /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see
+ /// cref="T:System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The current instance has already been disposed.
+ /// </exception>
+ public void AddCount()
+ {
+ AddCount(1);
+ }
+
+ /// <summary>
+ /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one.
+ /// </summary>
+ /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is
+ /// already at zero. this will return false.</returns>
+ /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see
+ /// cref="T:System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool TryAddCount()
+ {
+ return TryAddCount(1);
+ }
+
+ /// <summary>
+ /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a specified
+ /// value.
+ /// </summary>
+ /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less than
+ /// 0.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The current instance is already
+ /// set.</exception>
+ /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see
+ /// cref="T:System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public void AddCount(int signalCount)
+ {
+ if (!TryAddCount(signalCount))
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyZero"));
+ }
+ }
+
+ /// <summary>
+ /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a
+ /// specified value.
+ /// </summary>
+ /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param>
+ /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is
+ /// already at zero this will return false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less
+ /// than 0.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The current instance is already
+ /// set.</exception>
+ /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see
+ /// cref="T:System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool TryAddCount(int signalCount)
+ {
+ if (signalCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("signalCount");
+ }
+
+ ThrowIfDisposed();
+
+ // Loop around until we successfully increment the count.
+ int observedCount;
+ SpinWait spin = new SpinWait();
+ while (true)
+ {
+ observedCount = m_currentCount;
+
+ if (observedCount <= 0)
+ {
+ return false;
+ }
+ else if (observedCount > (Int32.MaxValue - signalCount))
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyMax"));
+ }
+
+ // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning
+ // for this statement. This warning is clearly senseless for Interlocked operations.
+#pragma warning disable 0420
+ if (Interlocked.CompareExchange(ref m_currentCount, observedCount + signalCount, observedCount) == observedCount)
+#pragma warning restore 0420
+ {
+ break;
+ }
+
+ // The CAS failed. Spin briefly and try again.
+ spin.SpinOnce();
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Resets the <see cref="CurrentCount"/> to the value of <see cref="InitialCount"/>.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed..</exception>
+ public void Reset()
+ {
+ Reset(m_initialCount);
+ }
+
+ /// <summary>
+ /// Resets the <see cref="CurrentCount"/> to a specified value.
+ /// </summary>
+ /// <param name="count">The number of signals required to set the <see
+ /// cref="T:System.Threading.CountdownEvent"/>.</param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="count"/> is
+ /// less than 0.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has alread been disposed.</exception>
+ public void Reset(int count)
+ {
+ ThrowIfDisposed();
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+
+ m_currentCount = count;
+ m_initialCount = count;
+
+ if (count == 0)
+ {
+ m_event.Set();
+ }
+ else
+ {
+ m_event.Reset();
+ }
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set.
+ /// </summary>
+ /// <remarks>
+ /// The caller of this method blocks indefinitely until the current instance is set. The caller will
+ /// return immediately if the event is currently in a set state.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public void Wait()
+ {
+ Wait(Timeout.Infinite, new CancellationToken());
+ }
+
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, while
+ /// observing a <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <remarks>
+ /// The caller of this method blocks indefinitely until the current instance is set. The caller will
+ /// return immediately if the event is currently in a set state. If the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> being observed
+ /// is canceled during the wait operation, an <see cref="T:System.OperationCanceledException"/>
+ /// will be thrown.
+ /// </remarks>
+ /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has been
+ /// canceled.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public void Wait(CancellationToken cancellationToken)
+ {
+ Wait(Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a
+ /// <see cref="T:System.TimeSpan"/> to measure the time interval.
+ /// </summary>
+ /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of
+ /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to
+ /// wait indefinitely.</param>
+ /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool Wait(TimeSpan timeout)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("timeout");
+ }
+
+ return Wait((int)totalMilliseconds, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using
+ /// a <see cref="T:System.TimeSpan"/> to measure the time interval, while observing a
+ /// <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of
+ /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to
+ /// wait indefinitely.</param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has
+ /// been canceled.</exception>
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("timeout");
+ }
+
+ return Wait((int)totalMilliseconds, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a
+ /// 32-bit signed integer to measure the time interval.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+ /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public bool Wait(int millisecondsTimeout)
+ {
+ return Wait(millisecondsTimeout, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a
+ /// 32-bit signed integer to measure the time interval, while observing a
+ /// <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has
+ /// been canceled.</exception>
+ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout");
+ }
+
+ ThrowIfDisposed();
+ cancellationToken.ThrowIfCancellationRequested();
+
+ bool returnValue = IsSet;
+
+ // If not completed yet, wait on the event.
+ if (!returnValue)
+ {
+ // ** the actual wait
+ returnValue = m_event.Wait(millisecondsTimeout, cancellationToken);
+ //the Wait will throw OCE itself if the token is canceled.
+ }
+
+ return returnValue;
+ }
+
+ // --------------------------------------
+ // Private methods
+
+
+ /// <summary>
+ /// Throws an exception if the latch has been disposed.
+ /// </summary>
+ private void ThrowIfDisposed()
+ {
+ if (m_disposed)
+ {
+ throw new ObjectDisposedException("CountdownEvent");
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/EventResetMode.cs b/src/mscorlib/src/System/Threading/EventResetMode.cs
new file mode 100644
index 0000000000..edafab9bb5
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/EventResetMode.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+** Enum: EventResetMode
+**
+**
+** Purpose: Enum to determine the Event type to create
+**
+**
+=============================================================================*/
+
+
+namespace System.Threading
+{
+ using System.Runtime.InteropServices;
+ [ComVisibleAttribute(false)]
+ public enum EventResetMode
+ {
+ AutoReset = 0,
+ ManualReset = 1
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/EventWaitHandle.cs b/src/mscorlib/src/System/Threading/EventWaitHandle.cs
new file mode 100644
index 0000000000..f56da1fa26
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/EventWaitHandle.cs
@@ -0,0 +1,297 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Base class for representing Events
+**
+**
+=============================================================================*/
+
+
+#if !FEATURE_MACL
+namespace System.Security.AccessControl
+{
+ public class EventWaitHandleSecurity
+ {
+ }
+ public enum EventWaitHandleRights
+ {
+ }
+}
+#endif
+
+namespace System.Threading
+{
+ using System;
+ using System.Threading;
+ using System.Runtime.CompilerServices;
+ using System.Security.Permissions;
+ using System.IO;
+ using Microsoft.Win32;
+ using Microsoft.Win32.SafeHandles;
+ using System.Runtime.InteropServices;
+ using System.Runtime.Versioning;
+ using System.Security.AccessControl;
+ using System.Diagnostics.Contracts;
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [ComVisibleAttribute(true)]
+ public class EventWaitHandle : WaitHandle
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public EventWaitHandle(bool initialState, EventResetMode mode) : this(initialState,mode,null) { }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public EventWaitHandle(bool initialState, EventResetMode mode, string name)
+ {
+ if(name != null)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives"));
+#else
+ if (System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+#endif
+ }
+ Contract.EndContractBlock();
+
+ SafeWaitHandle _handle = null;
+ switch(mode)
+ {
+ case EventResetMode.ManualReset:
+ _handle = Win32Native.CreateEvent(null, true, initialState, name);
+ break;
+ case EventResetMode.AutoReset:
+ _handle = Win32Native.CreateEvent(null, false, initialState, name);
+ break;
+
+ default:
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name));
+ };
+
+ if (_handle.IsInvalid)
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+
+ _handle.SetHandleAsInvalid();
+ if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name));
+
+ __Error.WinIOError(errorCode, name);
+ }
+ SetHandleInternal(_handle);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew)
+ : this(initialState, mode, name, out createdNew, null)
+ {
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public unsafe EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity)
+ {
+ if(name != null)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives"));
+#else
+ if (System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+#endif
+ }
+ Contract.EndContractBlock();
+ Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
+#if FEATURE_MACL
+ // For ACL's, get the security descriptor from the EventWaitHandleSecurity.
+ if (eventSecurity != null) {
+ secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
+ secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
+
+ byte[] sd = eventSecurity.GetSecurityDescriptorBinaryForm();
+ byte* pSecDescriptor = stackalloc byte[sd.Length];
+ Buffer.Memcpy(pSecDescriptor, 0, sd, 0, sd.Length);
+ secAttrs.pSecurityDescriptor = pSecDescriptor;
+ }
+#endif
+
+ SafeWaitHandle _handle = null;
+ Boolean isManualReset;
+ switch(mode)
+ {
+ case EventResetMode.ManualReset:
+ isManualReset = true;
+ break;
+ case EventResetMode.AutoReset:
+ isManualReset = false;
+ break;
+
+ default:
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name));
+ };
+
+ _handle = Win32Native.CreateEvent(secAttrs, isManualReset, initialState, name);
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (_handle.IsInvalid)
+ {
+
+ _handle.SetHandleAsInvalid();
+ if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name));
+
+ __Error.WinIOError(errorCode, name);
+ }
+ createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
+ SetHandleInternal(_handle);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private EventWaitHandle(SafeWaitHandle handle)
+ {
+ SetHandleInternal(handle);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static EventWaitHandle OpenExisting(string name)
+ {
+#if !FEATURE_MACL
+ return OpenExisting(name, (EventWaitHandleRights)0);
+#else
+ return OpenExisting(name, EventWaitHandleRights.Modify | EventWaitHandleRights.Synchronize);
+#endif
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights)
+ {
+ EventWaitHandle result;
+ switch (OpenExistingWorker(name, rights, out result))
+ {
+ case OpenExistingResult.NameNotFound:
+ throw new WaitHandleCannotBeOpenedException();
+
+ case OpenExistingResult.NameInvalid:
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
+
+ case OpenExistingResult.PathNotFound:
+ __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, "");
+ return result; //never executes
+
+ default:
+ return result;
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static bool TryOpenExisting(string name, out EventWaitHandle result)
+ {
+#if !FEATURE_MACL
+ return OpenExistingWorker(name, (EventWaitHandleRights)0, out result) == OpenExistingResult.Success;
+#else
+ return OpenExistingWorker(name, EventWaitHandleRights.Modify | EventWaitHandleRights.Synchronize, out result) == OpenExistingResult.Success;
+#endif
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static bool TryOpenExisting(string name, EventWaitHandleRights rights, out EventWaitHandle result)
+ {
+ return OpenExistingWorker(name, rights, out result) == OpenExistingResult.Success;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ private static OpenExistingResult OpenExistingWorker(string name, EventWaitHandleRights rights, out EventWaitHandle result)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives"));
+#else
+ if (name == null)
+ {
+ throw new ArgumentNullException("name", Environment.GetResourceString("ArgumentNull_WithParamName"));
+ }
+
+ if(name.Length == 0)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "name");
+ }
+
+ if(null != name && System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+
+ Contract.EndContractBlock();
+
+ result = null;
+
+#if FEATURE_MACL
+ SafeWaitHandle myHandle = Win32Native.OpenEvent((int) rights, false, name);
+#else
+ SafeWaitHandle myHandle = Win32Native.OpenEvent(Win32Native.EVENT_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
+#endif
+
+ if (myHandle.IsInvalid)
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if(Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode)
+ return OpenExistingResult.NameNotFound;
+ if (Win32Native.ERROR_PATH_NOT_FOUND == errorCode)
+ return OpenExistingResult.PathNotFound;
+ if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ return OpenExistingResult.NameInvalid;
+ //this is for passed through Win32Native Errors
+ __Error.WinIOError(errorCode,"");
+ }
+ result = new EventWaitHandle(myHandle);
+ return OpenExistingResult.Success;
+#endif
+ }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public bool Reset()
+ {
+ bool res = Win32Native.ResetEvent(safeWaitHandle);
+ if (!res)
+ __Error.WinIOError();
+ return res;
+ }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public bool Set()
+ {
+ bool res = Win32Native.SetEvent(safeWaitHandle);
+
+ if (!res)
+ __Error.WinIOError();
+
+ return res;
+ }
+
+#if FEATURE_MACL
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public EventWaitHandleSecurity GetAccessControl()
+ {
+ return new EventWaitHandleSecurity(safeWaitHandle, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void SetAccessControl(EventWaitHandleSecurity eventSecurity)
+ {
+ if (eventSecurity == null)
+ throw new ArgumentNullException("eventSecurity");
+ Contract.EndContractBlock();
+
+ eventSecurity.Persist(safeWaitHandle);
+ }
+#endif
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/ExecutionContext.cs b/src/mscorlib/src/System/Threading/ExecutionContext.cs
new file mode 100644
index 0000000000..0440368608
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ExecutionContext.cs
@@ -0,0 +1,1398 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+/*============================================================
+**
+**
+**
+** Purpose: Capture execution context for a thread
+**
+**
+===========================================================*/
+namespace System.Threading
+{
+ using System;
+ using System.Security;
+ using System.Runtime.Remoting;
+#if FEATURE_IMPERSONATION
+ using System.Security.Principal;
+#endif
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Reflection;
+ using System.Runtime.ExceptionServices;
+ using System.Runtime.Serialization;
+ using System.Security.Permissions;
+#if FEATURE_REMOTING
+ using System.Runtime.Remoting.Messaging;
+#endif // FEATURE_REMOTING
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+
+#if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+#endif
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public delegate void ContextCallback(Object state);
+
+#if FEATURE_CORECLR
+
+ [SecurityCritical]
+ internal struct ExecutionContextSwitcher
+ {
+ internal ExecutionContext m_ec;
+ internal SynchronizationContext m_sc;
+
+ internal void Undo(Thread currentThread)
+ {
+ Contract.Assert(currentThread == Thread.CurrentThread);
+
+ // The common case is that these have not changed, so avoid the cost of a write if not needed.
+ if (currentThread.SynchronizationContext != m_sc)
+ {
+ currentThread.SynchronizationContext = m_sc;
+ }
+
+ if (currentThread.ExecutionContext != m_ec)
+ {
+ ExecutionContext.Restore(currentThread, m_ec);
+ }
+ }
+ }
+
+ public sealed class ExecutionContext : IDisposable
+ {
+ private static readonly ExecutionContext Default = new ExecutionContext();
+
+ private readonly Dictionary<IAsyncLocal, object> m_localValues;
+ private readonly IAsyncLocal[] m_localChangeNotifications;
+
+ private ExecutionContext()
+ {
+ m_localValues = new Dictionary<IAsyncLocal, object>();
+ m_localChangeNotifications = Array.Empty<IAsyncLocal>();
+ }
+
+ private ExecutionContext(Dictionary<IAsyncLocal, object> localValues, IAsyncLocal[] localChangeNotifications)
+ {
+ m_localValues = localValues;
+ m_localChangeNotifications = localChangeNotifications;
+ }
+
+ [SecuritySafeCritical]
+ public static ExecutionContext Capture()
+ {
+ return Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default;
+ }
+
+ [SecurityCritical]
+ [HandleProcessCorruptedStateExceptions]
+ public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state)
+ {
+ if (executionContext == null)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NullContext"));
+
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher);
+ try
+ {
+ EstablishCopyOnWriteScope(currentThread, ref ecsw);
+ ExecutionContext.Restore(currentThread, executionContext);
+ callback(state);
+ }
+ catch
+ {
+ // Note: we have a "catch" rather than a "finally" because we want
+ // to stop the first pass of EH here. That way we can restore the previous
+ // context before any of our callers' EH filters run. That means we need to
+ // end the scope separately in the non-exceptional case below.
+ ecsw.Undo(currentThread);
+ throw;
+ }
+ ecsw.Undo(currentThread);
+ }
+
+ [SecurityCritical]
+ internal static void Restore(Thread currentThread, ExecutionContext executionContext)
+ {
+ Contract.Assert(currentThread == Thread.CurrentThread);
+
+ ExecutionContext previous = currentThread.ExecutionContext ?? Default;
+ currentThread.ExecutionContext = executionContext;
+
+ // New EC could be null if that's what ECS.Undo saved off.
+ // For the purposes of dealing with context change, treat this as the default EC
+ executionContext = executionContext ?? Default;
+
+ if (previous != executionContext)
+ {
+ OnContextChanged(previous, executionContext);
+ }
+ }
+
+ [SecurityCritical]
+ static internal void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw)
+ {
+ Contract.Assert(currentThread == Thread.CurrentThread);
+
+ ecsw.m_ec = currentThread.ExecutionContext;
+ ecsw.m_sc = currentThread.SynchronizationContext;
+ }
+
+ [SecurityCritical]
+ [HandleProcessCorruptedStateExceptions]
+ private static void OnContextChanged(ExecutionContext previous, ExecutionContext current)
+ {
+ Contract.Assert(previous != null);
+ Contract.Assert(current != null);
+ Contract.Assert(previous != current);
+
+ foreach (IAsyncLocal local in previous.m_localChangeNotifications)
+ {
+ object previousValue;
+ object currentValue;
+ previous.m_localValues.TryGetValue(local, out previousValue);
+ current.m_localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+
+ if (current.m_localChangeNotifications != previous.m_localChangeNotifications)
+ {
+ try
+ {
+ foreach (IAsyncLocal local in current.m_localChangeNotifications)
+ {
+ // If the local has a value in the previous context, we already fired the event for that local
+ // in the code above.
+ object previousValue;
+ if (!previous.m_localValues.TryGetValue(local, out previousValue))
+ {
+ object currentValue;
+ current.m_localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Environment.FailFast(
+ Environment.GetResourceString("ExecutionContext_ExceptionInAsyncLocalNotification"),
+ ex);
+ }
+ }
+ }
+
+ [SecurityCritical]
+ internal static object GetLocalValue(IAsyncLocal local)
+ {
+ ExecutionContext current = Thread.CurrentThread.ExecutionContext;
+ if (current == null)
+ return null;
+
+ object value;
+ current.m_localValues.TryGetValue(local, out value);
+ return value;
+ }
+
+ [SecurityCritical]
+ internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications)
+ {
+ ExecutionContext current = Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default;
+
+ object previousValue;
+ bool hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue);
+
+ if (previousValue == newValue)
+ return;
+
+ //
+ // Allocate a new Dictionary containing a copy of the old values, plus the new value. We have to do this manually to
+ // minimize allocations of IEnumerators, etc.
+ //
+ Dictionary<IAsyncLocal, object> newValues = new Dictionary<IAsyncLocal, object>(current.m_localValues.Count + (hadPreviousValue ? 0 : 1));
+
+ foreach (KeyValuePair<IAsyncLocal, object> pair in current.m_localValues)
+ newValues.Add(pair.Key, pair.Value);
+
+ newValues[local] = newValue;
+
+ //
+ // Either copy the change notification array, or create a new one, depending on whether we need to add a new item.
+ //
+ IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications;
+ if (needChangeNotifications)
+ {
+ if (hadPreviousValue)
+ {
+ Contract.Assert(Array.IndexOf(newChangeNotifications, local) >= 0);
+ }
+ else
+ {
+ int newNotificationIndex = newChangeNotifications.Length;
+ Array.Resize(ref newChangeNotifications, newNotificationIndex + 1);
+ newChangeNotifications[newNotificationIndex] = local;
+ }
+ }
+
+ Thread.CurrentThread.ExecutionContext = new ExecutionContext(newValues, newChangeNotifications);
+
+ if (needChangeNotifications)
+ {
+ local.OnValueChanged(previousValue, newValue, false);
+ }
+ }
+
+ #region Wrappers for CLR compat, to avoid ifdefs all over the BCL
+
+ [Flags]
+ internal enum CaptureOptions
+ {
+ None = 0x00,
+ IgnoreSyncCtx = 0x01,
+ OptimizeDefaultCase = 0x02,
+ }
+
+ [SecurityCritical]
+ internal static ExecutionContext Capture(ref StackCrawlMark stackMark, CaptureOptions captureOptions)
+ {
+ return Capture();
+ }
+
+ [SecuritySafeCritical]
+ [FriendAccessAllowed]
+ internal static ExecutionContext FastCapture()
+ {
+ return Capture();
+ }
+
+ [SecurityCritical]
+ [FriendAccessAllowed]
+ internal static void Run(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx)
+ {
+ Run(executionContext, callback, state);
+ }
+
+ [SecurityCritical]
+ internal bool IsDefaultFTContext(bool ignoreSyncCtx)
+ {
+ return this == Default;
+ }
+
+ [SecuritySafeCritical]
+ public ExecutionContext CreateCopy()
+ {
+ return this; // since CoreCLR's ExecutionContext is immutable, we don't need to create copies.
+ }
+
+ public void Dispose()
+ {
+ // For CLR compat only
+ }
+
+ public static bool IsFlowSuppressed()
+ {
+ return false;
+ }
+
+ internal static ExecutionContext PreAllocatedDefault
+ {
+ [SecuritySafeCritical]
+ get { return ExecutionContext.Default; }
+ }
+
+ internal bool IsPreAllocatedDefault
+ {
+ get { return this == ExecutionContext.Default; }
+ }
+
+ #endregion
+ }
+
+#else // FEATURE_CORECLR
+
+ // Legacy desktop ExecutionContext implementation
+
+ internal struct ExecutionContextSwitcher
+ {
+ internal ExecutionContext.Reader outerEC; // previous EC we need to restore on Undo
+ internal bool outerECBelongsToScope;
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ internal SecurityContextSwitcher scsw;
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ internal Object hecsw;
+#if FEATURE_IMPERSONATION
+ internal WindowsIdentity wi;
+ internal bool cachedAlwaysFlowImpersonationPolicy;
+ internal bool wiIsValid;
+#endif
+ internal Thread thread;
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+#if FEATURE_CORRUPTING_EXCEPTIONS
+ [HandleProcessCorruptedStateExceptions]
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+ internal bool UndoNoThrow(Thread currentThread)
+ {
+ try
+ {
+ Undo(currentThread);
+ }
+ catch
+ {
+ return false;
+ }
+ return true;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal void Undo(Thread currentThread)
+ {
+ //
+ // Don't use an uninitialized switcher, or one that's already been used.
+ //
+ if (thread == null)
+ return; // Don't do anything
+
+ Contract.Assert(Thread.CurrentThread == this.thread);
+
+ //
+ // Restore the HostExecutionContext before restoring the ExecutionContext.
+ //
+#if FEATURE_CAS_POLICY
+ if (hecsw != null)
+ HostExecutionContextSwitcher.Undo(hecsw);
+#endif // FEATURE_CAS_POLICY
+
+ //
+ // restore the saved Execution Context. Note that this will also restore the
+ // SynchronizationContext, Logical/IllogicalCallContext, etc.
+ //
+ ExecutionContext.Reader innerEC = currentThread.GetExecutionContextReader();
+ currentThread.SetExecutionContext(outerEC, outerECBelongsToScope);
+
+#if DEBUG
+ try
+ {
+ currentThread.ForbidExecutionContextMutation = true;
+#endif
+
+ //
+ // Tell the SecurityContext to do the side-effects of restoration.
+ //
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ if (scsw.currSC != null)
+ {
+ // Any critical failure inside scsw will cause FailFast
+ scsw.Undo();
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+
+#if FEATURE_IMPERSONATION
+ if (wiIsValid)
+ SecurityContext.RestoreCurrentWI(outerEC, innerEC, wi, cachedAlwaysFlowImpersonationPolicy);
+#endif
+
+ thread = null; // this will prevent the switcher object being used again
+#if DEBUG
+ }
+ finally
+ {
+ currentThread.ForbidExecutionContextMutation = false;
+ }
+#endif
+ ExecutionContext.OnAsyncLocalContextChanged(innerEC.DangerousGetRawExecutionContext(), outerEC.DangerousGetRawExecutionContext());
+ }
+ }
+
+
+ public struct AsyncFlowControl: IDisposable
+ {
+ private bool useEC;
+ private ExecutionContext _ec;
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ private SecurityContext _sc;
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ private Thread _thread;
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ [SecurityCritical]
+ internal void Setup(SecurityContextDisableFlow flags)
+ {
+ useEC = false;
+ Thread currentThread = Thread.CurrentThread;
+ _sc = currentThread.GetMutableExecutionContext().SecurityContext;
+ _sc._disableFlow = flags;
+ _thread = currentThread;
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ [SecurityCritical]
+ internal void Setup()
+ {
+ useEC = true;
+ Thread currentThread = Thread.CurrentThread;
+ _ec = currentThread.GetMutableExecutionContext();
+ _ec.isFlowSuppressed = true;
+ _thread = currentThread;
+ }
+
+ public void Dispose()
+ {
+ Undo();
+ }
+
+ [SecuritySafeCritical]
+ public void Undo()
+ {
+ if (_thread == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCMultiple"));
+ }
+ if (_thread != Thread.CurrentThread)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCOtherThread"));
+ }
+ if (useEC)
+ {
+ if (Thread.CurrentThread.GetMutableExecutionContext() != _ec)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch"));
+ }
+ ExecutionContext.RestoreFlow();
+ }
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ else
+ {
+ if (!Thread.CurrentThread.GetExecutionContextReader().SecurityContext.IsSame(_sc))
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch"));
+ }
+ SecurityContext.RestoreFlow();
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ _thread = null;
+ }
+
+ public override int GetHashCode()
+ {
+ return _thread == null ? ToString().GetHashCode() : _thread.GetHashCode();
+ }
+
+ public override bool Equals(Object obj)
+ {
+ if (obj is AsyncFlowControl)
+ return Equals((AsyncFlowControl)obj);
+ else
+ return false;
+ }
+
+ public bool Equals(AsyncFlowControl obj)
+ {
+ return obj.useEC == useEC && obj._ec == _ec &&
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ obj._sc == _sc &&
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ obj._thread == _thread;
+ }
+
+ public static bool operator ==(AsyncFlowControl a, AsyncFlowControl b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(AsyncFlowControl a, AsyncFlowControl b)
+ {
+ return !(a == b);
+ }
+
+ }
+
+
+#if FEATURE_SERIALIZATION
+ [Serializable]
+#endif
+ public sealed class ExecutionContext : IDisposable, ISerializable
+ {
+ /*=========================================================================
+ ** Data accessed from managed code that needs to be defined in
+ ** ExecutionContextObject to maintain alignment between the two classes.
+ ** DON'T CHANGE THESE UNLESS YOU MODIFY ExecutionContextObject in vm\object.h
+ =========================================================================*/
+#if FEATURE_CAS_POLICY
+ private HostExecutionContext _hostExecutionContext;
+#endif // FEATURE_CAS_POLICY
+ private SynchronizationContext _syncContext;
+ private SynchronizationContext _syncContextNoFlow;
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ private SecurityContext _securityContext;
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+#if FEATURE_REMOTING
+ [System.Security.SecurityCritical] // auto-generated
+ private LogicalCallContext _logicalCallContext;
+ private IllogicalCallContext _illogicalCallContext; // this call context follows the physical thread
+#endif // #if FEATURE_REMOTING
+
+ enum Flags
+ {
+ None = 0x0,
+ IsNewCapture = 0x1,
+ IsFlowSuppressed = 0x2,
+ IsPreAllocatedDefault = 0x4
+ }
+ private Flags _flags;
+
+ private Dictionary<IAsyncLocal, object> _localValues;
+ private List<IAsyncLocal> _localChangeNotifications;
+
+ internal bool isNewCapture
+ {
+ get
+ {
+ return (_flags & (Flags.IsNewCapture | Flags.IsPreAllocatedDefault)) != Flags.None;
+ }
+ set
+ {
+ Contract.Assert(!IsPreAllocatedDefault);
+ if (value)
+ _flags |= Flags.IsNewCapture;
+ else
+ _flags &= ~Flags.IsNewCapture;
+ }
+ }
+ internal bool isFlowSuppressed
+ {
+ get
+ {
+ return (_flags & Flags.IsFlowSuppressed) != Flags.None;
+ }
+ set
+ {
+ Contract.Assert(!IsPreAllocatedDefault);
+ if (value)
+ _flags |= Flags.IsFlowSuppressed;
+ else
+ _flags &= ~Flags.IsFlowSuppressed;
+ }
+ }
+
+
+ private static readonly ExecutionContext s_dummyDefaultEC = new ExecutionContext(isPreAllocatedDefault: true);
+
+ static internal ExecutionContext PreAllocatedDefault
+ {
+ [SecuritySafeCritical]
+ get { return s_dummyDefaultEC; }
+ }
+
+ internal bool IsPreAllocatedDefault
+ {
+ get
+ {
+ // we use _flags instead of a direct comparison w/ s_dummyDefaultEC to avoid the static access on
+ // hot code paths.
+ if ((_flags & Flags.IsPreAllocatedDefault) != Flags.None)
+ {
+ Contract.Assert(this == s_dummyDefaultEC);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal ExecutionContext()
+ {
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal ExecutionContext(bool isPreAllocatedDefault)
+ {
+ if (isPreAllocatedDefault)
+ _flags = Flags.IsPreAllocatedDefault;
+ }
+
+ // Read-only wrapper around ExecutionContext. This enables safe reading of an ExecutionContext without accidentally modifying it.
+ internal struct Reader
+ {
+ ExecutionContext m_ec;
+
+ public Reader(ExecutionContext ec) { m_ec = ec; }
+
+ public ExecutionContext DangerousGetRawExecutionContext() { return m_ec; }
+
+ public bool IsNull { get { return m_ec == null; } }
+ [SecurityCritical]
+ public bool IsDefaultFTContext(bool ignoreSyncCtx) { return m_ec.IsDefaultFTContext(ignoreSyncCtx); }
+ public bool IsFlowSuppressed
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get { return IsNull ? false : m_ec.isFlowSuppressed; }
+ }
+ //public Thread Thread { get { return m_ec._thread; } }
+ public bool IsSame(ExecutionContext.Reader other) { return m_ec == other.m_ec; }
+
+ public SynchronizationContext SynchronizationContext { get { return IsNull ? null : m_ec.SynchronizationContext; } }
+ public SynchronizationContext SynchronizationContextNoFlow { get { return IsNull ? null : m_ec.SynchronizationContextNoFlow; } }
+
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ public SecurityContext.Reader SecurityContext
+ {
+ [SecurityCritical]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get { return new SecurityContext.Reader(IsNull ? null : m_ec.SecurityContext); }
+ }
+#endif
+
+#if FEATURE_REMOTING
+ public LogicalCallContext.Reader LogicalCallContext
+ {
+ [SecurityCritical]
+ get { return new LogicalCallContext.Reader(IsNull ? null : m_ec.LogicalCallContext); }
+ }
+
+ public IllogicalCallContext.Reader IllogicalCallContext
+ {
+ [SecurityCritical]
+ get { return new IllogicalCallContext.Reader(IsNull ? null : m_ec.IllogicalCallContext); }
+ }
+#endif
+
+ [SecurityCritical]
+ public object GetLocalValue(IAsyncLocal local)
+ {
+ if (IsNull)
+ return null;
+
+ if (m_ec._localValues == null)
+ return null;
+
+ object value;
+ m_ec._localValues.TryGetValue(local, out value);
+ return value;
+ }
+
+ [SecurityCritical]
+ public bool HasSameLocalValues(ExecutionContext other)
+ {
+ var thisLocalValues = IsNull ? null : m_ec._localValues;
+ var otherLocalValues = other == null ? null : other._localValues;
+ return thisLocalValues == otherLocalValues;
+ }
+
+ [SecurityCritical]
+ public bool HasLocalValues()
+ {
+ return !this.IsNull && m_ec._localValues != null;
+ }
+ }
+
+ [SecurityCritical]
+ internal static object GetLocalValue(IAsyncLocal local)
+ {
+ return Thread.CurrentThread.GetExecutionContextReader().GetLocalValue(local);
+ }
+
+ [SecurityCritical]
+ internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications)
+ {
+ ExecutionContext current = Thread.CurrentThread.GetMutableExecutionContext();
+
+ object previousValue = null;
+ bool hadPreviousValue = current._localValues != null && current._localValues.TryGetValue(local, out previousValue);
+
+ if (previousValue == newValue)
+ return;
+
+ if (current._localValues == null)
+ current._localValues = new Dictionary<IAsyncLocal, object>();
+ else
+ current._localValues = new Dictionary<IAsyncLocal, object>(current._localValues);
+
+ current._localValues[local] = newValue;
+
+ if (needChangeNotifications)
+ {
+ if (hadPreviousValue)
+ {
+ Contract.Assert(current._localChangeNotifications != null);
+ Contract.Assert(current._localChangeNotifications.Contains(local));
+ }
+ else
+ {
+ if (current._localChangeNotifications == null)
+ current._localChangeNotifications = new List<IAsyncLocal>();
+ else
+ current._localChangeNotifications = new List<IAsyncLocal>(current._localChangeNotifications);
+
+ current._localChangeNotifications.Add(local);
+ }
+
+ local.OnValueChanged(previousValue, newValue, false);
+ }
+ }
+
+ [SecurityCritical]
+ [HandleProcessCorruptedStateExceptions]
+ internal static void OnAsyncLocalContextChanged(ExecutionContext previous, ExecutionContext current)
+ {
+ List<IAsyncLocal> previousLocalChangeNotifications = (previous == null) ? null : previous._localChangeNotifications;
+ if (previousLocalChangeNotifications != null)
+ {
+ foreach (IAsyncLocal local in previousLocalChangeNotifications)
+ {
+ object previousValue = null;
+ if (previous != null && previous._localValues != null)
+ previous._localValues.TryGetValue(local, out previousValue);
+
+ object currentValue = null;
+ if (current != null && current._localValues != null)
+ current._localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+ }
+
+ List<IAsyncLocal> currentLocalChangeNotifications = (current == null) ? null : current._localChangeNotifications;
+ if (currentLocalChangeNotifications != null && currentLocalChangeNotifications != previousLocalChangeNotifications)
+ {
+ try
+ {
+ foreach (IAsyncLocal local in currentLocalChangeNotifications)
+ {
+ // If the local has a value in the previous context, we already fired the event for that local
+ // in the code above.
+ object previousValue = null;
+ if (previous == null ||
+ previous._localValues == null ||
+ !previous._localValues.TryGetValue(local, out previousValue))
+ {
+ object currentValue = null;
+ if (current != null && current._localValues != null)
+ current._localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Environment.FailFast(
+ Environment.GetResourceString("ExecutionContext_ExceptionInAsyncLocalNotification"),
+ ex);
+ }
+ }
+ }
+
+
+#if FEATURE_REMOTING
+ internal LogicalCallContext LogicalCallContext
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ get
+ {
+ if (_logicalCallContext == null)
+ {
+ _logicalCallContext = new LogicalCallContext();
+ }
+ return _logicalCallContext;
+ }
+ [System.Security.SecurityCritical] // auto-generated
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ _logicalCallContext = value;
+ }
+ }
+
+ internal IllogicalCallContext IllogicalCallContext
+ {
+ get
+ {
+ if (_illogicalCallContext == null)
+ {
+ _illogicalCallContext = new IllogicalCallContext();
+ }
+ return _illogicalCallContext;
+ }
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ _illogicalCallContext = value;
+ }
+ }
+#endif // #if FEATURE_REMOTING
+
+ internal SynchronizationContext SynchronizationContext
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get
+ {
+ return _syncContext;
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ _syncContext = value;
+ }
+ }
+
+ internal SynchronizationContext SynchronizationContextNoFlow
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get
+ {
+ return _syncContextNoFlow;
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ _syncContextNoFlow = value;
+ }
+ }
+
+#if FEATURE_CAS_POLICY
+ internal HostExecutionContext HostExecutionContext
+ {
+ get
+ {
+ return _hostExecutionContext;
+ }
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ _hostExecutionContext = value;
+ }
+ }
+#endif // FEATURE_CAS_POLICY
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ internal SecurityContext SecurityContext
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get
+ {
+ return _securityContext;
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ set
+ {
+ Contract.Assert(this != s_dummyDefaultEC);
+ // store the new security context
+ _securityContext = value;
+ // perform the reverse link too
+ if (value != null)
+ _securityContext.ExecutionContext = this;
+ }
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+
+
+ public void Dispose()
+ {
+ if(this.IsPreAllocatedDefault)
+ return; //Do nothing if this is the default context
+#if FEATURE_CAS_POLICY
+ if (_hostExecutionContext != null)
+ _hostExecutionContext.Dispose();
+#endif // FEATURE_CAS_POLICY
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ if (_securityContext != null)
+ _securityContext.Dispose();
+#endif //FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ }
+
+ [DynamicSecurityMethod]
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state)
+ {
+ if (executionContext == null)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NullContext"));
+ if (!executionContext.isNewCapture)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NotNewCaptureContext"));
+
+ Run(executionContext, callback, state, false);
+ }
+
+ // This method is special from a security perspective - the VM will not allow a stack walk to
+ // continue past the call to ExecutionContext.Run. If you change the signature to this method, make
+ // sure to update SecurityStackWalk::IsSpecialRunFrame in the VM to search for the new signature.
+ [DynamicSecurityMethod]
+ [SecurityCritical]
+ [FriendAccessAllowed]
+ internal static void Run(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx)
+ {
+ RunInternal(executionContext, callback, state, preserveSyncCtx);
+ }
+
+ // Actual implementation of Run is here, in a non-DynamicSecurityMethod, because the JIT seems to refuse to inline callees into
+ // a DynamicSecurityMethod.
+ [SecurityCritical]
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ [HandleProcessCorruptedStateExceptions]
+ internal static void RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx)
+ {
+ Contract.Assert(executionContext != null);
+ if (executionContext.IsPreAllocatedDefault)
+ {
+ Contract.Assert(executionContext.IsDefaultFTContext(preserveSyncCtx));
+ }
+ else
+ {
+ Contract.Assert(executionContext.isNewCapture);
+ executionContext.isNewCapture = false;
+ }
+
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher);
+
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ ExecutionContext.Reader ec = currentThread.GetExecutionContextReader();
+ if ( (ec.IsNull || ec.IsDefaultFTContext(preserveSyncCtx)) &&
+ #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ SecurityContext.CurrentlyInDefaultFTSecurityContext(ec) &&
+ #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ executionContext.IsDefaultFTContext(preserveSyncCtx) &&
+ ec.HasSameLocalValues(executionContext)
+ )
+ {
+ // Neither context is interesting, so we don't need to set the context.
+ // We do need to reset any changes made by the user's callback,
+ // so here we establish a "copy-on-write scope". Any changes will
+ // result in a copy of the context being made, preserving the original
+ // context.
+ EstablishCopyOnWriteScope(currentThread, true, ref ecsw);
+ }
+ else
+ {
+ if (executionContext.IsPreAllocatedDefault)
+ executionContext = new ExecutionContext();
+ ecsw = SetExecutionContext(executionContext, preserveSyncCtx);
+ }
+
+ //
+ // Call the user's callback
+ //
+ callback(state);
+ }
+ finally
+ {
+ ecsw.Undo(currentThread);
+ }
+ }
+
+ [SecurityCritical]
+ static internal void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw)
+ {
+ EstablishCopyOnWriteScope(currentThread, false, ref ecsw);
+ }
+
+ [SecurityCritical]
+ static private void EstablishCopyOnWriteScope(Thread currentThread, bool knownNullWindowsIdentity, ref ExecutionContextSwitcher ecsw)
+ {
+ Contract.Assert(currentThread == Thread.CurrentThread);
+
+ ecsw.outerEC = currentThread.GetExecutionContextReader();
+ ecsw.outerECBelongsToScope = currentThread.ExecutionContextBelongsToCurrentScope;
+
+#if FEATURE_IMPERSONATION
+ ecsw.cachedAlwaysFlowImpersonationPolicy = SecurityContext.AlwaysFlowImpersonationPolicy;
+ if (knownNullWindowsIdentity)
+ Contract.Assert(SecurityContext.GetCurrentWI(ecsw.outerEC, ecsw.cachedAlwaysFlowImpersonationPolicy) == null);
+ else
+ ecsw.wi = SecurityContext.GetCurrentWI(ecsw.outerEC, ecsw.cachedAlwaysFlowImpersonationPolicy);
+ ecsw.wiIsValid = true;
+#endif
+ currentThread.ExecutionContextBelongsToCurrentScope = false;
+ ecsw.thread = currentThread;
+ }
+
+
+ // Sets the given execution context object on the thread.
+ // Returns the previous one.
+ [System.Security.SecurityCritical] // auto-generated
+ [DynamicSecurityMethodAttribute()]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+#if FEATURE_CORRUPTING_EXCEPTIONS
+ [HandleProcessCorruptedStateExceptions]
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+ internal static ExecutionContextSwitcher SetExecutionContext(ExecutionContext executionContext, bool preserveSyncCtx)
+ {
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+
+ Contract.Assert(executionContext != null);
+ Contract.Assert(executionContext != s_dummyDefaultEC);
+
+ // Set up the switcher object to return;
+ ExecutionContextSwitcher ecsw = new ExecutionContextSwitcher();
+
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContext.Reader outerEC = currentThread.GetExecutionContextReader();
+
+ ecsw.thread = currentThread;
+ ecsw.outerEC = outerEC;
+ ecsw.outerECBelongsToScope = currentThread.ExecutionContextBelongsToCurrentScope;
+
+ if (preserveSyncCtx)
+ executionContext.SynchronizationContext = outerEC.SynchronizationContext;
+ executionContext.SynchronizationContextNoFlow = outerEC.SynchronizationContextNoFlow;
+
+ currentThread.SetExecutionContext(executionContext, belongsToCurrentScope: true);
+
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ OnAsyncLocalContextChanged(outerEC.DangerousGetRawExecutionContext(), executionContext);
+
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ //set the security context
+ SecurityContext sc = executionContext.SecurityContext;
+ if (sc != null)
+ {
+ // non-null SC: needs to be set
+ SecurityContext.Reader prevSeC = outerEC.SecurityContext;
+ ecsw.scsw = SecurityContext.SetSecurityContext(sc, prevSeC, false, ref stackMark);
+ }
+ else if (!SecurityContext.CurrentlyInDefaultFTSecurityContext(ecsw.outerEC))
+ {
+ // null incoming SC, but we're currently not in FT: use static FTSC to set
+ SecurityContext.Reader prevSeC = outerEC.SecurityContext;
+ ecsw.scsw = SecurityContext.SetSecurityContext(SecurityContext.FullTrustSecurityContext, prevSeC, false, ref stackMark);
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+#if FEATURE_CAS_POLICY
+ // set the Host Context
+ HostExecutionContext hostContext = executionContext.HostExecutionContext;
+ if (hostContext != null)
+ {
+ ecsw.hecsw = HostExecutionContextManager.SetHostExecutionContextInternal(hostContext);
+ }
+#endif // FEATURE_CAS_POLICY
+ }
+ catch
+ {
+ ecsw.UndoNoThrow(currentThread);
+ throw;
+ }
+ return ecsw;
+ }
+
+ //
+ // Public CreateCopy. Used to copy captured ExecutionContexts so they can be reused multiple times.
+ // This should only copy the portion of the context that we actually capture.
+ //
+ [SecuritySafeCritical]
+ public ExecutionContext CreateCopy()
+ {
+ if (!isNewCapture)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotCopyUsedContext"));
+ }
+ ExecutionContext ec = new ExecutionContext();
+ ec.isNewCapture = true;
+ ec._syncContext = _syncContext == null ? null : _syncContext.CreateCopy();
+ ec._localValues = _localValues;
+ ec._localChangeNotifications = _localChangeNotifications;
+#if FEATURE_CAS_POLICY
+ // capture the host execution context
+ ec._hostExecutionContext = _hostExecutionContext == null ? null : _hostExecutionContext.CreateCopy();
+#endif // FEATURE_CAS_POLICY
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ if (_securityContext != null)
+ {
+ ec._securityContext = _securityContext.CreateCopy();
+ ec._securityContext.ExecutionContext = ec;
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+
+#if FEATURE_REMOTING
+ if (this._logicalCallContext != null)
+ ec.LogicalCallContext = (LogicalCallContext)this.LogicalCallContext.Clone();
+
+ Contract.Assert(this._illogicalCallContext == null);
+#endif // #if FEATURE_REMOTING
+
+ return ec;
+ }
+
+ //
+ // Creates a complete copy, used for copy-on-write.
+ //
+ [SecuritySafeCritical]
+ internal ExecutionContext CreateMutableCopy()
+ {
+ Contract.Assert(!this.isNewCapture);
+
+ ExecutionContext ec = new ExecutionContext();
+
+ // We don't deep-copy the SyncCtx, since we're still in the same context after copy-on-write.
+ ec._syncContext = this._syncContext;
+ ec._syncContextNoFlow = this._syncContextNoFlow;
+
+#if FEATURE_CAS_POLICY
+ // capture the host execution context
+ ec._hostExecutionContext = this._hostExecutionContext == null ? null : _hostExecutionContext.CreateCopy();
+#endif // FEATURE_CAS_POLICY
+
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ if (_securityContext != null)
+ {
+ ec._securityContext = this._securityContext.CreateMutableCopy();
+ ec._securityContext.ExecutionContext = ec;
+ }
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+
+#if FEATURE_REMOTING
+ if (this._logicalCallContext != null)
+ ec.LogicalCallContext = (LogicalCallContext)this.LogicalCallContext.Clone();
+
+ if (this._illogicalCallContext != null)
+ ec.IllogicalCallContext = (IllogicalCallContext)this.IllogicalCallContext.CreateCopy();
+#endif // #if FEATURE_REMOTING
+
+ ec._localValues = this._localValues;
+ ec._localChangeNotifications = this._localChangeNotifications;
+ ec.isFlowSuppressed = this.isFlowSuppressed;
+
+ return ec;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static AsyncFlowControl SuppressFlow()
+ {
+ if (IsFlowSuppressed())
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes"));
+ }
+ Contract.EndContractBlock();
+ AsyncFlowControl afc = new AsyncFlowControl();
+ afc.Setup();
+ return afc;
+ }
+
+ [SecuritySafeCritical]
+ public static void RestoreFlow()
+ {
+ ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext();
+ if (!ec.isFlowSuppressed)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRestoreUnsupressedFlow"));
+ }
+ ec.isFlowSuppressed = false;
+ }
+
+ [Pure]
+ public static bool IsFlowSuppressed()
+ {
+ return Thread.CurrentThread.GetExecutionContextReader().IsFlowSuppressed;
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static ExecutionContext Capture()
+ {
+ // set up a stack mark for finding the caller
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ExecutionContext.Capture(ref stackMark, CaptureOptions.None);
+ }
+
+ //
+ // Captures an ExecutionContext with optimization for the "default" case, and captures a "null" synchronization context.
+ // When calling ExecutionContext.Run on the returned context, specify ignoreSyncCtx = true
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ [FriendAccessAllowed]
+ internal static ExecutionContext FastCapture()
+ {
+ // set up a stack mark for finding the caller
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ExecutionContext.Capture(ref stackMark, CaptureOptions.IgnoreSyncCtx | CaptureOptions.OptimizeDefaultCase);
+ }
+
+
+ [Flags]
+ internal enum CaptureOptions
+ {
+ None = 0x00,
+
+ IgnoreSyncCtx = 0x01, //Don't flow SynchronizationContext
+
+ OptimizeDefaultCase = 0x02, //Faster in the typical case, but can't show the result to users
+ // because they could modify the shared default EC.
+ // Use this only if you won't be exposing the captured EC to users.
+ }
+
+ // internal helper to capture the current execution context using a passed in stack mark
+ [System.Security.SecurityCritical] // auto-generated
+ static internal ExecutionContext Capture(ref StackCrawlMark stackMark, CaptureOptions options)
+ {
+ ExecutionContext.Reader ecCurrent = Thread.CurrentThread.GetExecutionContextReader();
+
+ // check to see if Flow is suppressed
+ if (ecCurrent.IsFlowSuppressed)
+ return null;
+
+ //
+ // Attempt to capture context. There may be nothing to capture...
+ //
+
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ // capture the security context
+ SecurityContext secCtxNew = SecurityContext.Capture(ecCurrent, ref stackMark);
+#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+#if FEATURE_CAS_POLICY
+ // capture the host execution context
+ HostExecutionContext hostCtxNew = HostExecutionContextManager.CaptureHostExecutionContext();
+#endif // FEATURE_CAS_POLICY
+
+ SynchronizationContext syncCtxNew = null;
+
+#if FEATURE_REMOTING
+ LogicalCallContext logCtxNew = null;
+#endif
+
+ if (!ecCurrent.IsNull)
+ {
+ // capture the sync context
+ if (0 == (options & CaptureOptions.IgnoreSyncCtx))
+ syncCtxNew = (ecCurrent.SynchronizationContext == null) ? null : ecCurrent.SynchronizationContext.CreateCopy();
+
+#if FEATURE_REMOTING
+ // copy over the Logical Call Context
+ if (ecCurrent.LogicalCallContext.HasInfo)
+ logCtxNew = ecCurrent.LogicalCallContext.Clone();
+#endif // #if FEATURE_REMOTING
+ }
+
+ Dictionary<IAsyncLocal, object> localValues = null;
+ List<IAsyncLocal> localChangeNotifications = null;
+ if (!ecCurrent.IsNull)
+ {
+ localValues = ecCurrent.DangerousGetRawExecutionContext()._localValues;
+ localChangeNotifications = ecCurrent.DangerousGetRawExecutionContext()._localChangeNotifications;
+ }
+
+ //
+ // If we didn't get anything but defaults, and we're allowed to return the
+ // dummy default EC, don't bother allocating a new context.
+ //
+ if (0 != (options & CaptureOptions.OptimizeDefaultCase) &&
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ secCtxNew == null &&
+#endif
+#if FEATURE_CAS_POLICY
+ hostCtxNew == null &&
+#endif // FEATURE_CAS_POLICY
+ syncCtxNew == null &&
+#if FEATURE_REMOTING
+ (logCtxNew == null || !logCtxNew.HasInfo) &&
+#endif // #if FEATURE_REMOTING
+ localValues == null &&
+ localChangeNotifications == null
+ )
+ {
+ return s_dummyDefaultEC;
+ }
+
+ //
+ // Allocate the new context, and fill it in.
+ //
+ ExecutionContext ecNew = new ExecutionContext();
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ ecNew.SecurityContext = secCtxNew;
+ if (ecNew.SecurityContext != null)
+ ecNew.SecurityContext.ExecutionContext = ecNew;
+#endif
+#if FEATURE_CAS_POLICY
+ ecNew._hostExecutionContext = hostCtxNew;
+#endif // FEATURE_CAS_POLICY
+ ecNew._syncContext = syncCtxNew;
+#if FEATURE_REMOTING
+ ecNew.LogicalCallContext = logCtxNew;
+#endif // #if FEATURE_REMOTING
+ ecNew._localValues = localValues;
+ ecNew._localChangeNotifications = localChangeNotifications;
+ ecNew.isNewCapture = true;
+
+ return ecNew;
+ }
+
+ //
+ // Implementation of ISerializable
+ //
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info==null)
+ throw new ArgumentNullException("info");
+ Contract.EndContractBlock();
+
+#if FEATURE_REMOTING
+ if (_logicalCallContext != null)
+ {
+ info.AddValue("LogicalCallContext", _logicalCallContext, typeof(LogicalCallContext));
+ }
+#endif // #if FEATURE_REMOTING
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private ExecutionContext(SerializationInfo info, StreamingContext context)
+ {
+ SerializationInfoEnumerator e = info.GetEnumerator();
+ while (e.MoveNext())
+ {
+#if FEATURE_REMOTING
+ if (e.Name.Equals("LogicalCallContext"))
+ {
+ _logicalCallContext = (LogicalCallContext) e.Value;
+ }
+#endif // #if FEATURE_REMOTING
+ }
+ } // ObjRef .ctor
+
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal bool IsDefaultFTContext(bool ignoreSyncCtx)
+ {
+#if FEATURE_CAS_POLICY
+ if (_hostExecutionContext != null)
+ return false;
+#endif // FEATURE_CAS_POLICY
+ if (!ignoreSyncCtx && _syncContext != null)
+ return false;
+#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+ if (_securityContext != null && !_securityContext.IsDefaultFTSecurityContext())
+ return false;
+#endif //#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
+#if FEATURE_REMOTING
+ if (_logicalCallContext != null && _logicalCallContext.HasInfo)
+ return false;
+ if (_illogicalCallContext != null && _illogicalCallContext.HasUserData)
+ return false;
+#endif //#if FEATURE_REMOTING
+ return true;
+ }
+ } // class ExecutionContext
+
+#endif //FEATURE_CORECLR
+}
+
+
diff --git a/src/mscorlib/src/System/Threading/IObjectHandle.cs b/src/mscorlib/src/System/Threading/IObjectHandle.cs
new file mode 100644
index 0000000000..464f06e52d
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/IObjectHandle.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** IObjectHandle defines the interface for unwrapping objects.
+** Objects that are marshal by value object can be returned through
+** an indirection allowing the caller to control when the
+** object is loaded into their domain. The caller can unwrap
+** the object from the indirection through this interface.
+**
+**
+===========================================================*/
+namespace System.Runtime.Remoting {
+
+ using System;
+ using System.Runtime.InteropServices;
+
+ [ InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
+ GuidAttribute("C460E2B4-E199-412a-8456-84DC3E4838C3") ]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IObjectHandle {
+ // Unwrap the object. Implementers of this interface
+ // typically have an indirect referece to another object.
+ Object Unwrap();
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/Interlocked.cs b/src/mscorlib/src/System/Threading/Interlocked.cs
new file mode 100644
index 0000000000..50cc766d61
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Interlocked.cs
@@ -0,0 +1,233 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+namespace System.Threading
+{
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.Versioning;
+ using System.Runtime;
+
+ // After much discussion, we decided the Interlocked class doesn't need
+ // any HPA's for synchronization or external threading. They hurt C#'s
+ // codegen for the yield keyword, and arguably they didn't protect much.
+ // Instead, they penalized people (and compilers) for writing threadsafe
+ // code.
+ public static class Interlocked
+ {
+ /******************************
+ * Increment
+ * Implemented: int
+ * long
+ *****************************/
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static int Increment(ref int location)
+ {
+ return Add(ref location, 1);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static long Increment(ref long location)
+ {
+ return Add(ref location, 1);
+ }
+
+ /******************************
+ * Decrement
+ * Implemented: int
+ * long
+ *****************************/
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static int Decrement(ref int location)
+ {
+ return Add(ref location, -1);
+ }
+
+ public static long Decrement(ref long location)
+ {
+ return Add(ref location, -1);
+ }
+
+ /******************************
+ * Exchange
+ * Implemented: int
+ * long
+ * float
+ * double
+ * Object
+ * IntPtr
+ *****************************/
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern int Exchange(ref int location1, int value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern long Exchange(ref long location1, long value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern float Exchange(ref float location1, float value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern double Exchange(ref double location1, double value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern Object Exchange(ref Object location1, Object value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern IntPtr Exchange(ref IntPtr location1, IntPtr value);
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ [System.Security.SecuritySafeCritical]
+ public static T Exchange<T>(ref T location1, T value) where T : class
+ {
+ _Exchange(__makeref(location1), __makeref(value));
+ //Since value is a local we use trash its data on return
+ // The Exchange replaces the data with new data
+ // so after the return "value" contains the original location1
+ //See ExchangeGeneric for more details
+ return value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ private static extern void _Exchange(TypedReference location1, TypedReference value);
+
+ /******************************
+ * CompareExchange
+ * Implemented: int
+ * long
+ * float
+ * double
+ * Object
+ * IntPtr
+ *****************************/
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern int CompareExchange(ref int location1, int value, int comparand);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern long CompareExchange(ref long location1, long value, long comparand);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern float CompareExchange(ref float location1, float value, float comparand);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical]
+ public static extern double CompareExchange(ref double location1, double value, double comparand);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern Object CompareExchange(ref Object location1, Object value, Object comparand);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ public static extern IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand);
+
+ /*****************************************************************
+ * CompareExchange<T>
+ *
+ * Notice how CompareExchange<T>() uses the __makeref keyword
+ * to create two TypedReferences before calling _CompareExchange().
+ * This is horribly slow. Ideally we would like CompareExchange<T>()
+ * to simply call CompareExchange(ref Object, Object, Object);
+ * however, this would require casting a "ref T" into a "ref Object",
+ * which is not legal in C#.
+ *
+ * Thus we opted to implement this in the JIT so that when it reads
+ * the method body for CompareExchange<T>() it gets back the
+ * following IL:
+ *
+ * ldarg.0
+ * ldarg.1
+ * ldarg.2
+ * call System.Threading.Interlocked::CompareExchange(ref Object, Object, Object)
+ * ret
+ *
+ * See getILIntrinsicImplementationForInterlocked() in VM\JitInterface.cpp
+ * for details.
+ *****************************************************************/
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ [System.Security.SecuritySafeCritical]
+ public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class
+ {
+ // _CompareExchange() passes back the value read from location1 via local named 'value'
+ _CompareExchange(__makeref(location1), __makeref(value), comparand);
+ return value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ private static extern void _CompareExchange(TypedReference location1, TypedReference value, Object comparand);
+
+ // BCL-internal overload that returns success via a ref bool param, useful for reliable spin locks.
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Security.SecuritySafeCritical]
+ internal static extern int CompareExchange(ref int location1, int value, int comparand, ref bool succeeded);
+
+ /******************************
+ * Add
+ * Implemented: int
+ * long
+ *****************************/
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal static extern int ExchangeAdd(ref int location1, int value);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern long ExchangeAdd(ref long location1, long value);
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static int Add(ref int location1, int value)
+ {
+ return ExchangeAdd(ref location1, value) + value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static long Add(ref long location1, long value)
+ {
+ return ExchangeAdd(ref location1, value) + value;
+ }
+
+ /******************************
+ * Read
+ *****************************/
+ public static long Read(ref long location)
+ {
+ return Interlocked.CompareExchange(ref location,0,0);
+ }
+
+
+ public static void MemoryBarrier()
+ {
+ Thread.MemoryBarrier();
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/LazyInitializer.cs b/src/mscorlib/src/System/Threading/LazyInitializer.cs
new file mode 100644
index 0000000000..c8e74e30e3
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/LazyInitializer.cs
@@ -0,0 +1,265 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// a set of lightweight static helpers for lazy initialization.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+namespace System.Threading
+{
+
+ /// <summary>
+ /// Specifies how a <see cref="T:System.Threading.Lazy{T}"/> instance should synchronize access among multiple threads.
+ /// </summary>
+ public enum LazyThreadSafetyMode
+ {
+ /// <summary>
+ /// This mode makes no guarantees around the thread-safety of the <see cref="T:System.Threading.Lazy{T}"/> instance. If used from multiple threads, the behavior of the <see cref="T:System.Threading.Lazy{T}"/> is undefined.
+ /// This mode should be used when a <see cref="T:System.Threading.Lazy{T}"/> is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial.
+ /// If valueFactory throws an exception when the <see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively
+ /// accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown.
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// When multiple threads attempt to simultaneously initialize a <see cref="T:System.Threading.Lazy{T}"/> instance, this mode allows each thread to execute the
+ /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the <see cref="T:System.Threading.Lazy{T}"/>.
+ /// Once initialized successfully, any future calls to Value will return the cached result. If valueFactory throws an exception on any thread, that exception will be
+ /// propagated out of Value. If any thread executes valueFactory without throwing an exception and, therefore, successfully sets the value, that value will be returned on
+ /// subsequent accesses to Value from any thread. If no thread succeeds in setting the value, IsValueCreated will remain false and subsequent accesses to Value will result in
+ /// the valueFactory delegate re-executing. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, an exception will NOT be thrown.
+ /// </summary>
+ PublicationOnly,
+
+ /// <summary>
+ /// This mode uses locks to ensure that only a single thread can initialize a <see cref="T:System.Threading.Lazy{T}"/> instance in a thread-safe manner. In general,
+ /// taken if this mode is used in conjunction with a <see cref="T:System.Threading.Lazy{T}"/> valueFactory delegate that uses locks internally, a deadlock can occur if not
+ /// handled carefully. If valueFactory throws an exception when the<see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on
+ /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown.
+ /// </summary>
+ ExecutionAndPublication
+ }
+ /// <summary>
+ /// Provides lazy initialization routines.
+ /// </summary>
+ /// <remarks>
+ /// These routines avoid needing to allocate a dedicated, lazy-initialization instance, instead using
+ /// references to ensure targets have been initialized as they are accessed.
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public static class LazyInitializer
+ {
+ /// <summary>
+ /// Initializes a target reference type with the type's default constructor if the target has not
+ /// already been initialized.
+ /// </summary>
+ /// <typeparam name="T">The refence type of the reference to be initialized.</typeparam>
+ /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not
+ /// already been initialized.</param>
+ /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
+ /// <exception cref="T:System.MissingMemberException">Type <typeparamref name="T"/> does not have a default
+ /// constructor.</exception>
+ /// <exception cref="T:System.MemberAccessException">
+ /// Permissions to access the constructor of type <typeparamref name="T"/> were missing.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// This method may only be used on reference types. To ensure initialization of value
+ /// types, see other overloads of EnsureInitialized.
+ /// </para>
+ /// <para>
+ /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.
+ /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
+ /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
+ /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine
+ /// if an object was not used and to then dispose of the object appropriately.
+ /// </para>
+ /// </remarks>
+ public static T EnsureInitialized<T>(ref T target) where T : class
+ {
+ // Fast path.
+ if (Volatile.Read<T>(ref target) != null)
+ {
+ return target;
+ }
+
+ return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector);
+ }
+
+ /// <summary>
+ /// Initializes a target reference type using the specified function if it has not already been
+ /// initialized.
+ /// </summary>
+ /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
+ /// <param name="target">The reference of type <typeparamref name="T"/> to initialize if it has not
+ /// already been initialized.</param>
+ /// <param name="valueFactory">The <see cref="T:System.Func{T}"/> invoked to initialize the
+ /// reference.</param>
+ /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
+ /// <exception cref="T:System.MissingMemberException">Type <typeparamref name="T"/> does not have a
+ /// default constructor.</exception>
+ /// <exception cref="T:System.InvalidOperationException"><paramref name="valueFactory"/> returned
+ /// null.</exception>
+ /// <remarks>
+ /// <para>
+ /// This method may only be used on reference types, and <paramref name="valueFactory"/> may
+ /// not return a null reference (Nothing in Visual Basic). To ensure initialization of value types or
+ /// to allow null reference types, see other overloads of EnsureInitialized.
+ /// </para>
+ /// <para>
+ /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.
+ /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
+ /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
+ /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine
+ /// if an object was not used and to then dispose of the object appropriately.
+ /// </para>
+ /// </remarks>
+ public static T EnsureInitialized<T>(ref T target, Func<T> valueFactory) where T : class
+ {
+ // Fast path.
+ if (Volatile.Read<T>(ref target) != null)
+ {
+ return target;
+ }
+
+ return EnsureInitializedCore<T>(ref target, valueFactory);
+ }
+
+ /// <summary>
+ /// Initialize the target using the given delegate (slow path).
+ /// </summary>
+ /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
+ /// <param name="target">The variable that need to be initialized</param>
+ /// <param name="valueFactory">The delegate that will be executed to initialize the target</param>
+ /// <returns>The initialized variable</returns>
+ private static T EnsureInitializedCore<T>(ref T target, Func<T> valueFactory) where T : class
+ {
+ T value = valueFactory();
+ if (value == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation"));
+ }
+
+ Interlocked.CompareExchange(ref target, value, null);
+ Contract.Assert(target != null);
+ return target;
+ }
+
+
+ /// <summary>
+ /// Initializes a target reference or value type with its default constructor if it has not already
+ /// been initialized.
+ /// </summary>
+ /// <typeparam name="T">The type of the reference to be initialized.</typeparam>
+ /// <param name="target">A reference or value of type <typeparamref name="T"/> to initialize if it
+ /// has not already been initialized.</param>
+ /// <param name="initialized">A reference to a boolean that determines whether the target has already
+ /// been initialized.</param>
+ /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
+ /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
+ /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
+ public static T EnsureInitialized<T>(ref T target, ref bool initialized, ref object syncLock)
+ {
+ // Fast path.
+ if (Volatile.Read(ref initialized))
+ {
+ return target;
+ }
+
+ return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, LazyHelpers<T>.s_activatorFactorySelector);
+ }
+
+ /// <summary>
+ /// Initializes a target reference or value type with a specified function if it has not already been
+ /// initialized.
+ /// </summary>
+ /// <typeparam name="T">The type of the reference to be initialized.</typeparam>
+ /// <param name="target">A reference or value of type <typeparamref name="T"/> to initialize if it
+ /// has not already been initialized.</param>
+ /// <param name="initialized">A reference to a boolean that determines whether the target has already
+ /// been initialized.</param>
+ /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
+ /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
+ /// <param name="valueFactory">The <see cref="T:System.Func{T}"/> invoked to initialize the
+ /// reference or value.</param>
+ /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
+ public static T EnsureInitialized<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
+ {
+ // Fast path.
+ if (Volatile.Read(ref initialized))
+ {
+ return target;
+ }
+
+
+ return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, valueFactory);
+ }
+
+ /// <summary>
+ /// Ensure the target is initialized and return the value (slow path). This overload permits nulls
+ /// and also works for value type targets. Uses the supplied function to create the value.
+ /// </summary>
+ /// <typeparam name="T">The type of target.</typeparam>
+ /// <param name="target">A reference to the target to be initialized.</param>
+ /// <param name="initialized">A reference to a location tracking whether the target has been initialized.</param>
+ /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
+ /// a new object will be instantiated.</param>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> to invoke in order to produce the lazily-initialized value.
+ /// </param>
+ /// <returns>The initialized object.</returns>
+ private static T EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
+ {
+ // Lazily initialize the lock if necessary.
+ object slock = syncLock;
+ if (slock == null)
+ {
+ object newLock = new object();
+ slock = Interlocked.CompareExchange(ref syncLock, newLock, null);
+ if (slock == null)
+ {
+ slock = newLock;
+ }
+ }
+
+ // Now double check that initialization is still required.
+ lock (slock)
+ {
+ if (!Volatile.Read(ref initialized))
+ {
+ target = valueFactory();
+ Volatile.Write(ref initialized, true);
+ }
+ }
+
+ return target;
+ }
+
+ }
+
+ // Caches the activation selector function to avoid delegate allocations.
+ static class LazyHelpers<T>
+ {
+ internal static Func<T> s_activatorFactorySelector = new Func<T>(ActivatorFactorySelector);
+
+ private static T ActivatorFactorySelector()
+ {
+ try
+ {
+ return (T)Activator.CreateInstance(typeof(T));
+ }
+ catch (MissingMethodException)
+ {
+ throw new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT"));
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/LockCookie.cs b/src/mscorlib/src/System/Threading/LockCookie.cs
new file mode 100644
index 0000000000..c1fbfd828e
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/LockCookie.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*============================================================
+**
+**
+**
+** Purpose: Defines the lock that implements
+** single-writer/multiple-reader semantics
+**
+**
+===========================================================*/
+
+namespace System.Threading {
+
+ using System;
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public struct LockCookie
+ {
+ private int _dwFlags;
+ private int _dwWriterSeqNum;
+ private int _wReaderAndWriterLevel;
+ private int _dwThreadID;
+
+ public override int GetHashCode()
+ {
+ return _dwFlags + _dwWriterSeqNum + _wReaderAndWriterLevel + _dwThreadID;
+ }
+
+ public override bool Equals(Object obj)
+ {
+ if (obj is LockCookie)
+ return Equals((LockCookie)obj);
+ else
+ return false;
+ }
+
+ public bool Equals(LockCookie obj)
+ {
+ return obj._dwFlags == _dwFlags && obj._dwWriterSeqNum == _dwWriterSeqNum &&
+ obj._wReaderAndWriterLevel == _wReaderAndWriterLevel && obj._dwThreadID == _dwThreadID;
+ }
+
+ public static bool operator ==(LockCookie a, LockCookie b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(LockCookie a, LockCookie b)
+ {
+ return !(a == b);
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/LockRecursionException.cs b/src/mscorlib/src/System/Threading/LockRecursionException.cs
new file mode 100644
index 0000000000..c5e3146cbc
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/LockRecursionException.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+//
+//
+//
+// Purpose:
+// This exception represents a failed attempt to recursively
+// acquire a lock, because the particular lock kind doesn't
+// support it in its current state.
+============================================================*/
+
+namespace System.Threading
+{
+ using System;
+ using System.Runtime.Serialization;
+ using System.Runtime.CompilerServices;
+
+ [Serializable]
+ [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
+#if !FEATURE_CORECLR
+ [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
+#endif
+ public class LockRecursionException : System.Exception
+ {
+ public LockRecursionException() { }
+ public LockRecursionException(string message) : base(message) { }
+ protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ public LockRecursionException(string message, Exception innerException) : base(message, innerException) { }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/ManualResetEvent.cs b/src/mscorlib/src/System/Threading/ManualResetEvent.cs
new file mode 100644
index 0000000000..504cfb423c
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ManualResetEvent.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: An example of a WaitHandle class
+**
+**
+=============================================================================*/
+namespace System.Threading {
+
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime.InteropServices;
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class ManualResetEvent : EventWaitHandle
+ {
+ public ManualResetEvent(bool initialState) : base(initialState,EventResetMode.ManualReset){}
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs
new file mode 100644
index 0000000000..b6114ef917
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs
@@ -0,0 +1,813 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// SlimManualResetEvent.cs
+//
+//
+// An manual-reset event that mixes a little spinning with a true Win32 event.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics;
+using System.Security.Permissions;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading
+{
+
+ // ManualResetEventSlim wraps a manual-reset event internally with a little bit of
+ // spinning. When an event will be set imminently, it is often advantageous to avoid
+ // a 4k+ cycle context switch in favor of briefly spinning. Therefore we layer on to
+ // a brief amount of spinning that should, on the average, make using the slim event
+ // cheaper than using Win32 events directly. This can be reset manually, much like
+ // a Win32 manual-reset would be.
+ //
+ // Notes:
+ // We lazily allocate the Win32 event internally. Therefore, the caller should
+ // always call Dispose to clean it up, just in case. This API is a no-op of the
+ // event wasn't allocated, but if it was, ensures that the event goes away
+ // eagerly, instead of waiting for finalization.
+
+ /// <summary>
+ /// Provides a slimmed down version of <see cref="T:System.Threading.ManualResetEvent"/>.
+ /// </summary>
+ /// <remarks>
+ /// All public and protected members of <see cref="ManualResetEventSlim"/> are thread-safe and may be used
+ /// concurrently from multiple threads, with the exception of Dispose, which
+ /// must only be used when all other operations on the <see cref="ManualResetEventSlim"/> have
+ /// completed, and Reset, which should only be used when no other threads are
+ /// accessing the event.
+ /// </remarks>
+ [ComVisible(false)]
+ [DebuggerDisplay("Set = {IsSet}")]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class ManualResetEventSlim : IDisposable
+ {
+ // These are the default spin counts we use on single-proc and MP machines.
+ private const int DEFAULT_SPIN_SP = 1;
+ private const int DEFAULT_SPIN_MP = SpinWait.YIELD_THRESHOLD;
+
+ private volatile object m_lock;
+ // A lock used for waiting and pulsing. Lazily initialized via EnsureLockObjectCreated()
+
+ private volatile ManualResetEvent m_eventObj; // A true Win32 event used for waiting.
+
+ // -- State -- //
+ //For a packed word a uint would seem better, but Interlocked.* doesn't support them as uint isn't CLS-compliant.
+ private volatile int m_combinedState; //ie a UInt32. Used for the state items listed below.
+
+ //1-bit for signalled state
+ private const int SignalledState_BitMask = unchecked((int)0x80000000);//1000 0000 0000 0000 0000 0000 0000 0000
+ private const int SignalledState_ShiftCount = 31;
+
+ //1-bit for disposed state
+ private const int Dispose_BitMask = unchecked((int)0x40000000);//0100 0000 0000 0000 0000 0000 0000 0000
+
+ //11-bits for m_spinCount
+ private const int SpinCountState_BitMask = unchecked((int)0x3FF80000); //0011 1111 1111 1000 0000 0000 0000 0000
+ private const int SpinCountState_ShiftCount = 19;
+ private const int SpinCountState_MaxValue = (1 << 11) - 1; //2047
+
+ //19-bits for m_waiters. This allows support of 512K threads waiting which should be ample
+ private const int NumWaitersState_BitMask = unchecked((int)0x0007FFFF); // 0000 0000 0000 0111 1111 1111 1111 1111
+ private const int NumWaitersState_ShiftCount = 0;
+ private const int NumWaitersState_MaxValue = (1 << 19) - 1; //512K-1
+ // ----------- //
+
+#if DEBUG
+ private static int s_nextId; // The next id that will be given out.
+ private int m_id = Interlocked.Increment(ref s_nextId); // A unique id for debugging purposes only.
+ private long m_lastSetTime;
+ private long m_lastResetTime;
+#endif
+
+ /// <summary>
+ /// Gets the underlying <see cref="T:System.Threading.WaitHandle"/> object for this <see
+ /// cref="ManualResetEventSlim"/>.
+ /// </summary>
+ /// <value>The underlying <see cref="T:System.Threading.WaitHandle"/> event object fore this <see
+ /// cref="ManualResetEventSlim"/>.</value>
+ /// <remarks>
+ /// Accessing this property forces initialization of an underlying event object if one hasn't
+ /// already been created. To simply wait on this <see cref="ManualResetEventSlim"/>,
+ /// the public Wait methods should be preferred.
+ /// </remarks>
+ public WaitHandle WaitHandle
+ {
+
+ get
+ {
+ ThrowIfDisposed();
+ if (m_eventObj == null)
+ {
+ // Lazily initialize the event object if needed.
+ LazyInitializeEvent();
+ }
+
+ return m_eventObj;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the event is set.
+ /// </summary>
+ /// <value>true if the event has is set; otherwise, false.</value>
+ public bool IsSet
+ {
+ get
+ {
+ return 0 != ExtractStatePortion(m_combinedState, SignalledState_BitMask);
+ }
+
+ private set
+ {
+ UpdateStateAtomically(((value) ? 1 : 0) << SignalledState_ShiftCount, SignalledState_BitMask);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of spin waits that will be occur before falling back to a true wait.
+ /// </summary>
+ public int SpinCount
+ {
+ get
+ {
+ return ExtractStatePortionAndShiftRight(m_combinedState, SpinCountState_BitMask, SpinCountState_ShiftCount);
+ }
+
+ private set
+ {
+ Contract.Assert(value >= 0, "SpinCount is a restricted-width integer. The value supplied is outside the legal range.");
+ Contract.Assert(value <= SpinCountState_MaxValue, "SpinCount is a restricted-width integer. The value supplied is outside the legal range.");
+ // Don't worry about thread safety because it's set one time from the constructor
+ m_combinedState = (m_combinedState & ~SpinCountState_BitMask) | (value << SpinCountState_ShiftCount);
+ }
+ }
+
+ /// <summary>
+ /// How many threads are waiting.
+ /// </summary>
+ private int Waiters
+ {
+ get
+ {
+ return ExtractStatePortionAndShiftRight(m_combinedState, NumWaitersState_BitMask, NumWaitersState_ShiftCount);
+ }
+
+ set
+ {
+ //setting to <0 would indicate an internal flaw, hence Assert is appropriate.
+ Contract.Assert(value >= 0, "NumWaiters should never be less than zero. This indicates an internal error.");
+
+ // it is possible for the max number of waiters to be exceeded via user-code, hence we use a real exception here.
+ if (value >= NumWaitersState_MaxValue)
+ throw new InvalidOperationException(String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_TooManyWaiters"), NumWaitersState_MaxValue));
+
+ UpdateStateAtomically(value << NumWaitersState_ShiftCount, NumWaitersState_BitMask);
+ }
+
+ }
+
+ //-----------------------------------------------------------------------------------
+ // Constructs a new event, optionally specifying the initial state and spin count.
+ // The defaults are that the event is unsignaled and some reasonable default spin.
+ //
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManualResetEventSlim"/>
+ /// class with an initial state of nonsignaled.
+ /// </summary>
+ public ManualResetEventSlim()
+ : this(false)
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManualResetEventSlim"/>
+ /// class with a Boolen value indicating whether to set the intial state to signaled.
+ /// </summary>
+ /// <param name="initialState">true to set the initial state signaled; false to set the initial state
+ /// to nonsignaled.</param>
+ public ManualResetEventSlim(bool initialState)
+ {
+ // Specify the defualt spin count, and use default spin if we're
+ // on a multi-processor machine. Otherwise, we won't.
+ Initialize(initialState, DEFAULT_SPIN_MP);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManualResetEventSlim"/>
+ /// class with a Boolen value indicating whether to set the intial state to signaled and a specified
+ /// spin count.
+ /// </summary>
+ /// <param name="initialState">true to set the initial state to signaled; false to set the initial state
+ /// to nonsignaled.</param>
+ /// <param name="spinCount">The number of spin waits that will occur before falling back to a true
+ /// wait.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="spinCount"/> is less than
+ /// 0 or greater than the maximum allowed value.</exception>
+ public ManualResetEventSlim(bool initialState, int spinCount)
+ {
+ if (spinCount < 0)
+ {
+ throw new ArgumentOutOfRangeException("spinCount");
+ }
+
+ if (spinCount > SpinCountState_MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(
+ "spinCount",
+ String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_SpinCountOutOfRange"), SpinCountState_MaxValue));
+ }
+
+ // We will suppress default spin because the user specified a count.
+ Initialize(initialState, spinCount);
+ }
+
+ /// <summary>
+ /// Initializes the internal state of the event.
+ /// </summary>
+ /// <param name="initialState">Whether the event is set initially or not.</param>
+ /// <param name="spinCount">The spin count that decides when the event will block.</param>
+ private void Initialize(bool initialState, int spinCount)
+ {
+ this.m_combinedState = initialState ? (1 << SignalledState_ShiftCount) : 0;
+ //the spinCount argument has been validated by the ctors.
+ //but we now sanity check our predefined constants.
+ Contract.Assert(DEFAULT_SPIN_SP >= 0, "Internal error - DEFAULT_SPIN_SP is outside the legal range.");
+ Contract.Assert(DEFAULT_SPIN_SP <= SpinCountState_MaxValue, "Internal error - DEFAULT_SPIN_SP is outside the legal range.");
+
+ SpinCount = PlatformHelper.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount;
+
+ }
+
+ /// <summary>
+ /// Helper to ensure the lock object is created before first use.
+ /// </summary>
+ private void EnsureLockObjectCreated()
+ {
+ Contract.Ensures(m_lock != null);
+
+ if (m_lock != null)
+ return;
+
+ object newObj = new object();
+ Interlocked.CompareExchange(ref m_lock, newObj, null); // failure is benign. Someone else set the value.
+ }
+
+ /// <summary>
+ /// This method lazily initializes the event object. It uses CAS to guarantee that
+ /// many threads racing to call this at once don't result in more than one event
+ /// being stored and used. The event will be signaled or unsignaled depending on
+ /// the state of the thin-event itself, with synchronization taken into account.
+ /// </summary>
+ /// <returns>True if a new event was created and stored, false otherwise.</returns>
+ private bool LazyInitializeEvent()
+ {
+ bool preInitializeIsSet = IsSet;
+ ManualResetEvent newEventObj = new ManualResetEvent(preInitializeIsSet);
+
+ // We have to CAS this in case we are racing with another thread. We must
+ // guarantee only one event is actually stored in this field.
+ if (Interlocked.CompareExchange(ref m_eventObj, newEventObj, null) != null)
+ {
+ // Someone else set the value due to a race condition. Destroy the garbage event.
+ newEventObj.Close();
+
+ return false;
+ }
+ else
+ {
+
+ // Now that the event is published, verify that the state hasn't changed since
+ // we snapped the preInitializeState. Another thread could have done that
+ // between our initial observation above and here. The barrier incurred from
+ // the CAS above (in addition to m_state being volatile) prevents this read
+ // from moving earlier and being collapsed with our original one.
+ bool currentIsSet = IsSet;
+ if (currentIsSet != preInitializeIsSet)
+ {
+ Contract.Assert(currentIsSet,
+ "The only safe concurrent transition is from unset->set: detected set->unset.");
+
+ // We saw it as unsignaled, but it has since become set.
+ lock (newEventObj)
+ {
+ // If our event hasn't already been disposed of, we must set it.
+ if (m_eventObj == newEventObj)
+ {
+ newEventObj.Set();
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Sets the state of the event to signaled, which allows one or more threads waiting on the event to
+ /// proceed.
+ /// </summary>
+ public void Set()
+ {
+ Set(false);
+ }
+
+ /// <summary>
+ /// Private helper to actually perform the Set.
+ /// </summary>
+ /// <param name="duringCancellation">Indicates whether we are calling Set() during cancellation.</param>
+ /// <exception cref="T:System.OperationCanceledException">The object has been canceled.</exception>
+ private void Set(bool duringCancellation)
+ {
+ // We need to ensure that IsSet=true does not get reordered past the read of m_eventObj
+ // This would be a legal movement according to the .NET memory model.
+ // The code is safe as IsSet involves an Interlocked.CompareExchange which provides a full memory barrier.
+ IsSet = true;
+
+ // If there are waiting threads, we need to pulse them.
+ if (Waiters > 0)
+ {
+ Contract.Assert(m_lock != null); //if waiters>0, then m_lock has already been created.
+ lock (m_lock)
+ {
+
+ Monitor.PulseAll(m_lock);
+ }
+ }
+
+ ManualResetEvent eventObj = m_eventObj;
+
+ //Design-decision: do not set the event if we are in cancellation -> better to deadlock than to wake up waiters incorrectly
+ //It would be preferable to wake up the event and have it throw OCE. This requires MRE to implement cancellation logic
+
+ if (eventObj != null && !duringCancellation)
+ {
+ // We must surround this call to Set in a lock. The reason is fairly subtle.
+ // Sometimes a thread will issue a Wait and wake up after we have set m_state,
+ // but before we have gotten around to setting m_eventObj (just below). That's
+ // because Wait first checks m_state and will only access the event if absolutely
+ // necessary. However, the coding pattern { event.Wait(); event.Dispose() } is
+ // quite common, and we must support it. If the waiter woke up and disposed of
+ // the event object before the setter has finished, however, we would try to set a
+ // now-disposed Win32 event. Crash! To deal with this race condition, we use a lock to
+ // protect access to the event object when setting and disposing of it. We also
+ // double-check that the event has not become null in the meantime when in the lock.
+
+ lock (eventObj)
+ {
+ if (m_eventObj != null)
+ {
+ // If somebody is waiting, we must set the event.
+ m_eventObj.Set();
+ }
+ }
+ }
+
+#if DEBUG
+ m_lastSetTime = DateTime.UtcNow.Ticks;
+#endif
+ }
+
+ /// <summary>
+ /// Sets the state of the event to nonsignaled, which causes threads to block.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="ManualResetEventSlim"/>, <see cref="Reset()"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ public void Reset()
+ {
+ ThrowIfDisposed();
+ // If there's an event, reset it.
+ if (m_eventObj != null)
+ {
+ m_eventObj.Reset();
+ }
+
+ // There is a race condition here. If another thread Sets the event, we will get into a state
+ // where m_state will be unsignaled, yet the Win32 event object will have been signaled.
+ // This could cause waiting threads to wake up even though the event is in an
+ // unsignaled state. This is fine -- those that are calling Reset concurrently are
+ // responsible for doing "the right thing" -- e.g. rechecking the condition and
+ // resetting the event manually.
+
+ // And finally set our state back to unsignaled.
+ IsSet = false;
+
+#if DEBUG
+ m_lastResetTime = DateTime.UtcNow.Ticks;
+#endif
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ /// <remarks>
+ /// The caller of this method blocks indefinitely until the current instance is set. The caller will
+ /// return immediately if the event is currently in a set state.
+ /// </remarks>
+ public void Wait()
+ {
+ Wait(Timeout.Infinite, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> receives a signal,
+ /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledExcepton"><paramref name="cancellationToken"/> was
+ /// canceled.</exception>
+ /// <remarks>
+ /// The caller of this method blocks indefinitely until the current instance is set. The caller will
+ /// return immediately if the event is currently in a set state.
+ /// </remarks>
+ public void Wait(CancellationToken cancellationToken)
+ {
+ Wait(Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a
+ /// <see cref="T:System.TimeSpan"/> to measure the time interval.
+ /// </summary>
+ /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ public bool Wait(TimeSpan timeout)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("timeout");
+ }
+
+ return Wait((int)totalMilliseconds, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a
+ /// <see cref="T:System.TimeSpan"/> to measure the time interval, while observing a <see
+ /// cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="T:System.Threading.OperationCanceledException"><paramref
+ /// name="cancellationToken"/> was canceled.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException("timeout");
+ }
+
+ return Wait((int)totalMilliseconds, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a
+ /// 32-bit signed integer to measure the time interval.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+ /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ public bool Wait(int millisecondsTimeout)
+ {
+ return Wait(millisecondsTimeout, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a
+ /// 32-bit signed integer to measure the time interval, while observing a <see
+ /// cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The maximum number of waiters has been exceeded.
+ /// </exception>
+ /// <exception cref="T:System.Threading.OperationCanceledException"><paramref
+ /// name="cancellationToken"/> was canceled.</exception>
+ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ ThrowIfDisposed();
+ cancellationToken.ThrowIfCancellationRequested(); // an early convenience check
+
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout");
+ }
+
+ if (!IsSet)
+ {
+ if (millisecondsTimeout == 0)
+ {
+ // For 0-timeouts, we just return immediately.
+ return false;
+ }
+
+
+ // We spin briefly before falling back to allocating and/or waiting on a true event.
+ uint startTime = 0;
+ bool bNeedTimeoutAdjustment = false;
+ int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary.
+
+ if (millisecondsTimeout != Timeout.Infinite)
+ {
+ // We will account for time spent spinning, so that we can decrement it from our
+ // timeout. In most cases the time spent in this section will be negligible. But
+ // we can't discount the possibility of our thread being switched out for a lengthy
+ // period of time. The timeout adjustments only take effect when and if we actually
+ // decide to block in the kernel below.
+
+ startTime = TimeoutHelper.GetTime();
+ bNeedTimeoutAdjustment = true;
+ }
+
+ //spin
+ int HOW_MANY_SPIN_BEFORE_YIELD = 10;
+ int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5;
+ int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20;
+
+ int spinCount = SpinCount;
+ for (int i = 0; i < spinCount; i++)
+ {
+ if (IsSet)
+ {
+ return true;
+ }
+
+ else if (i < HOW_MANY_SPIN_BEFORE_YIELD)
+ {
+ if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2)
+ {
+ Thread.Yield();
+ }
+ else
+ {
+ Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
+ }
+ }
+ else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0)
+ {
+ Thread.Sleep(1);
+ }
+ else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0)
+ {
+ Thread.Sleep(0);
+ }
+ else
+ {
+ Thread.Yield();
+ }
+
+ if (i >= 100 && i % 10 == 0) // check the cancellation token if the user passed a very large spin count
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+
+ // Now enter the lock and wait.
+ EnsureLockObjectCreated();
+
+ // We must register and deregister the token outside of the lock, to avoid deadlocks.
+ using (cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCallback, this))
+ {
+ lock (m_lock)
+ {
+ // Loop to cope with spurious wakeups from other waits being canceled
+ while (!IsSet)
+ {
+ // If our token was canceled, we must throw and exit.
+ cancellationToken.ThrowIfCancellationRequested();
+
+ //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled)
+ if (bNeedTimeoutAdjustment)
+ {
+ realMillisecondsTimeout = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
+ if (realMillisecondsTimeout <= 0)
+ return false;
+ }
+
+ // There is a race condition that Set will fail to see that there are waiters as Set does not take the lock,
+ // so after updating waiters, we must check IsSet again.
+ // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the
+ // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange
+ // operation which provides a full memory barrier.
+ // If we see IsSet=false, then we are guaranteed that Set() will see that we are
+ // waiting and will pulse the monitor correctly.
+
+ Waiters = Waiters + 1;
+
+ if (IsSet) //This check must occur after updating Waiters.
+ {
+ Waiters--; //revert the increment.
+ return true;
+ }
+
+ // Now finally perform the wait.
+ try
+ {
+ // ** the actual wait **
+ if (!Monitor.Wait(m_lock, realMillisecondsTimeout))
+ return false; //return immediately if the timeout has expired.
+ }
+ finally
+ {
+ // Clean up: we're done waiting.
+ Waiters = Waiters - 1;
+ }
+
+ // Now just loop back around, and the right thing will happen. Either:
+ // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait)
+ // or 2. the wait was successful. (the loop will break)
+
+ }
+ }
+ }
+ } // automatically disposes (and deregisters) the callback
+
+ return true; //done. The wait was satisfied.
+ }
+
+ /// <summary>
+ /// Releases all resources used by the current instance of <see cref="ManualResetEventSlim"/>.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="ManualResetEventSlim"/>, <see cref="Dispose()"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, releases the unmanaged resources used by the
+ /// <see cref="ManualResetEventSlim"/>, and optionally releases the managed resources.
+ /// </summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources;
+ /// false to release only unmanaged resources.</param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="ManualResetEventSlim"/>, <see cref="Dispose(Boolean)"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ if ((m_combinedState & Dispose_BitMask) != 0)
+ return; // already disposed
+
+ m_combinedState |= Dispose_BitMask; //set the dispose bit
+ if (disposing)
+ {
+ // We will dispose of the event object. We do this under a lock to protect
+ // against the race condition outlined in the Set method above.
+ ManualResetEvent eventObj = m_eventObj;
+ if (eventObj != null)
+ {
+ lock (eventObj)
+ {
+ eventObj.Close();
+ m_eventObj = null;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Throw ObjectDisposedException if the MRES is disposed
+ /// </summary>
+ private void ThrowIfDisposed()
+ {
+ if ((m_combinedState & Dispose_BitMask) != 0)
+ throw new ObjectDisposedException(Environment.GetResourceString("ManualResetEventSlim_Disposed"));
+ }
+
+ /// <summary>
+ /// Private helper method to wake up waiters when a cancellationToken gets canceled.
+ /// </summary>
+ private static Action<object> s_cancellationTokenCallback = new Action<object>(CancellationTokenCallback);
+ private static void CancellationTokenCallback(object obj)
+ {
+ ManualResetEventSlim mre = obj as ManualResetEventSlim;
+ Contract.Assert(mre != null, "Expected a ManualResetEventSlim");
+ Contract.Assert(mre.m_lock != null); //the lock should have been created before this callback is registered for use.
+ lock (mre.m_lock)
+ {
+ Monitor.PulseAll(mre.m_lock); // awaken all waiters
+ }
+ }
+
+ /// <summary>
+ /// Private helper method for updating parts of a bit-string state value.
+ /// Mainly called from the IsSet and Waiters properties setters
+ /// </summary>
+ /// <remarks>
+ /// Note: the parameter types must be int as CompareExchange cannot take a Uint
+ /// </remarks>
+ /// <param name="newBits">The new value</param>
+ /// <param name="updateBitsMask">The mask used to set the bits</param>
+ private void UpdateStateAtomically(int newBits, int updateBitsMask)
+ {
+ SpinWait sw = new SpinWait();
+
+ Contract.Assert((newBits | updateBitsMask) == updateBitsMask, "newBits do not fall within the updateBitsMask.");
+
+ do
+ {
+ int oldState = m_combinedState; // cache the old value for testing in CAS
+
+ // Procedure:(1) zero the updateBits. eg oldState = [11111111] flag= [00111000] newState = [11000111]
+ // then (2) map in the newBits. eg [11000111] newBits=00101000, newState=[11101111]
+ int newState = (oldState & ~updateBitsMask) | newBits;
+
+ if (Interlocked.CompareExchange(ref m_combinedState, newState, oldState) == oldState)
+ {
+ return;
+ }
+
+ sw.SpinOnce();
+ } while (true);
+ }
+
+ /// <summary>
+ /// Private helper method - performs Mask and shift, particular helpful to extract a field from a packed word.
+ /// eg ExtractStatePortionAndShiftRight(0x12345678, 0xFF000000, 24) => 0x12, ie extracting the top 8-bits as a simple integer
+ ///
+ /// ?? is there a common place to put this rather than being private to MRES?
+ /// </summary>
+ /// <param name="state"></param>
+ /// <param name="mask"></param>
+ /// <param name="rightBitShiftCount"></param>
+ /// <returns></returns>
+ private static int ExtractStatePortionAndShiftRight(int state, int mask, int rightBitShiftCount)
+ {
+ //convert to uint before shifting so that right-shift does not replicate the sign-bit,
+ //then convert back to int.
+ return unchecked((int)(((uint)(state & mask)) >> rightBitShiftCount));
+ }
+
+ /// <summary>
+ /// Performs a Mask operation, but does not perform the shift.
+ /// This is acceptable for boolean values for which the shift is unnecessary
+ /// eg (val &amp; Mask) != 0 is an appropriate way to extract a boolean rather than using
+ /// ((val &amp; Mask) &gt;&gt; shiftAmount) == 1
+ ///
+ /// ?? is there a common place to put this rather than being private to MRES?
+ /// </summary>
+ /// <param name="state"></param>
+ /// <param name="mask"></param>
+ private static int ExtractStatePortion(int state, int mask)
+ {
+ return state & mask;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Monitor.cs b/src/mscorlib/src/System/Threading/Monitor.cs
new file mode 100644
index 0000000000..415948b425
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Monitor.cs
@@ -0,0 +1,256 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Synchronizes access to a shared resource or region of code in a multi-threaded
+** program.
+**
+**
+=============================================================================*/
+
+
+namespace System.Threading {
+
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime;
+ using System.Runtime.Remoting;
+ using System.Threading;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public static class Monitor
+ {
+ /*=========================================================================
+ ** Obtain the monitor lock of obj. Will block if another thread holds the lock
+ ** Will not block if the current thread holds the lock,
+ ** however the caller must ensure that the same number of Exit
+ ** calls are made as there were Enter calls.
+ **
+ ** Exceptions: ArgumentNullException if object is null.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern void Enter(Object obj);
+
+
+ // Use a ref bool instead of out to ensure that unverifiable code must
+ // initialize this value to something. If we used out, the value
+ // could be uninitialized if we threw an exception in our prolog.
+ // The JIT should inline this method to allow check of lockTaken argument to be optimized out
+ // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
+ public static void Enter(Object obj, ref bool lockTaken)
+ {
+ if (lockTaken)
+ ThrowLockTakenException();
+
+ ReliableEnter(obj, ref lockTaken);
+ Contract.Assert(lockTaken);
+ }
+
+ private static void ThrowLockTakenException()
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_MustBeFalse"), "lockTaken");
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void ReliableEnter(Object obj, ref bool lockTaken);
+
+
+
+ /*=========================================================================
+ ** Release the monitor lock. If one or more threads are waiting to acquire the
+ ** lock, and the current thread has executed as many Exits as
+ ** Enters, one of the threads will be unblocked and allowed to proceed.
+ **
+ ** Exceptions: ArgumentNullException if object is null.
+ ** SynchronizationLockException if the current thread does not
+ ** own the lock.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static extern void Exit(Object obj);
+
+ /*=========================================================================
+ ** Similar to Enter, but will never block. That is, if the current thread can
+ ** acquire the monitor lock without blocking, it will do so and TRUE will
+ ** be returned. Otherwise FALSE will be returned.
+ **
+ ** Exceptions: ArgumentNullException if object is null.
+ =========================================================================*/
+ public static bool TryEnter(Object obj)
+ {
+ bool lockTaken = false;
+ TryEnter(obj, 0, ref lockTaken);
+ return lockTaken;
+ }
+
+ // The JIT should inline this method to allow check of lockTaken argument to be optimized out
+ // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
+ public static void TryEnter(Object obj, ref bool lockTaken)
+ {
+ if (lockTaken)
+ ThrowLockTakenException();
+
+ ReliableEnterTimeout(obj, 0, ref lockTaken);
+ }
+
+ /*=========================================================================
+ ** Version of TryEnter that will block, but only up to a timeout period
+ ** expressed in milliseconds. If timeout == Timeout.Infinite the method
+ ** becomes equivalent to Enter.
+ **
+ ** Exceptions: ArgumentNullException if object is null.
+ ** ArgumentException if timeout < 0.
+ =========================================================================*/
+ // The JIT should inline this method to allow check of lockTaken argument to be optimized out
+ // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
+ public static bool TryEnter(Object obj, int millisecondsTimeout)
+ {
+ bool lockTaken = false;
+ TryEnter(obj, millisecondsTimeout, ref lockTaken);
+ return lockTaken;
+ }
+
+ private static int MillisecondsTimeoutFromTimeSpan(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long)Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ return (int)tm;
+ }
+
+ public static bool TryEnter(Object obj, TimeSpan timeout)
+ {
+ return TryEnter(obj, MillisecondsTimeoutFromTimeSpan(timeout));
+ }
+
+ // The JIT should inline this method to allow check of lockTaken argument to be optimized out
+ // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
+ public static void TryEnter(Object obj, int millisecondsTimeout, ref bool lockTaken)
+ {
+ if (lockTaken)
+ ThrowLockTakenException();
+
+ ReliableEnterTimeout(obj, millisecondsTimeout, ref lockTaken);
+ }
+
+ public static void TryEnter(Object obj, TimeSpan timeout, ref bool lockTaken)
+ {
+ if (lockTaken)
+ ThrowLockTakenException();
+
+ ReliableEnterTimeout(obj, MillisecondsTimeoutFromTimeSpan(timeout), ref lockTaken);
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void ReliableEnterTimeout(Object obj, int timeout, ref bool lockTaken);
+
+ [System.Security.SecuritySafeCritical]
+ public static bool IsEntered(object obj)
+ {
+ if (obj == null)
+ throw new ArgumentNullException("obj");
+
+ return IsEnteredNative(obj);
+ }
+
+ [System.Security.SecurityCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool IsEnteredNative(Object obj);
+
+ /*========================================================================
+ ** Waits for notification from the object (via a Pulse/PulseAll).
+ ** timeout indicates how long to wait before the method returns.
+ ** This method acquires the monitor waithandle for the object
+ ** If this thread holds the monitor lock for the object, it releases it.
+ ** On exit from the method, it obtains the monitor lock back.
+ ** If exitContext is true then the synchronization domain for the context
+ ** (if in a synchronized context) is exited before the wait and reacquired
+ **
+ ** Exceptions: ArgumentNullException if object is null.
+ ========================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool ObjWait(bool exitContext, int millisecondsTimeout, Object obj);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static bool Wait(Object obj, int millisecondsTimeout, bool exitContext)
+ {
+ if (obj == null)
+ throw (new ArgumentNullException("obj"));
+ return ObjWait(exitContext, millisecondsTimeout, obj);
+ }
+
+ public static bool Wait(Object obj, TimeSpan timeout, bool exitContext)
+ {
+ return Wait(obj, MillisecondsTimeoutFromTimeSpan(timeout), exitContext);
+ }
+
+ public static bool Wait(Object obj, int millisecondsTimeout)
+ {
+ return Wait(obj, millisecondsTimeout, false);
+ }
+
+ public static bool Wait(Object obj, TimeSpan timeout)
+ {
+ return Wait(obj, MillisecondsTimeoutFromTimeSpan(timeout), false);
+ }
+
+ public static bool Wait(Object obj)
+ {
+ return Wait(obj, Timeout.Infinite, false);
+ }
+
+ /*========================================================================
+ ** Sends a notification to a single waiting object.
+ * Exceptions: SynchronizationLockException if this method is not called inside
+ * a synchronized block of code.
+ ========================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void ObjPulse(Object obj);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void Pulse(Object obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException("obj");
+ }
+ Contract.EndContractBlock();
+
+ ObjPulse(obj);
+ }
+ /*========================================================================
+ ** Sends a notification to all waiting objects.
+ ========================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void ObjPulseAll(Object obj);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void PulseAll(Object obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException("obj");
+ }
+ Contract.EndContractBlock();
+
+ ObjPulseAll(obj);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Mutex.cs b/src/mscorlib/src/System/Threading/Mutex.cs
new file mode 100644
index 0000000000..2e9b68176d
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Mutex.cs
@@ -0,0 +1,488 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: synchronization primitive that can also be used for interprocess synchronization
+**
+**
+=============================================================================*/
+namespace System.Threading
+{
+ using System;
+ using System.Threading;
+ using System.Runtime.CompilerServices;
+ using System.Security.Permissions;
+ using System.IO;
+ using Microsoft.Win32;
+ using Microsoft.Win32.SafeHandles;
+ using System.Runtime.InteropServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.Versioning;
+ using System.Security;
+ using System.Diagnostics.Contracts;
+
+#if FEATURE_MACL
+ using System.Security.AccessControl;
+#endif
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [ComVisible(true)]
+ public sealed class Mutex : WaitHandle
+ {
+ static bool dummyBool;
+
+#if !FEATURE_MACL
+ public class MutexSecurity {
+ }
+#endif
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public Mutex(bool initiallyOwned, String name, out bool createdNew)
+ : this(initiallyOwned, name, out createdNew, (MutexSecurity)null)
+ {
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public unsafe Mutex(bool initiallyOwned, String name, out bool createdNew, MutexSecurity mutexSecurity)
+ {
+ if (name == string.Empty)
+ {
+ // Empty name is treated as an unnamed mutex. Set to null, and we will check for null from now on.
+ name = null;
+ }
+#if !PLATFORM_UNIX
+ if (name != null && System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+#endif
+ Contract.EndContractBlock();
+ Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
+#if FEATURE_MACL
+ // For ACL's, get the security descriptor from the MutexSecurity.
+ if (mutexSecurity != null) {
+
+ secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
+ secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
+
+ byte[] sd = mutexSecurity.GetSecurityDescriptorBinaryForm();
+ byte* pSecDescriptor = stackalloc byte[sd.Length];
+ Buffer.Memcpy(pSecDescriptor, 0, sd, 0, sd.Length);
+ secAttrs.pSecurityDescriptor = pSecDescriptor;
+ }
+#endif
+
+ CreateMutexWithGuaranteedCleanup(initiallyOwned, name, out createdNew, secAttrs);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal Mutex(bool initiallyOwned, String name, out bool createdNew, Win32Native.SECURITY_ATTRIBUTES secAttrs)
+ {
+ if (name == string.Empty)
+ {
+ // Empty name is treated as an unnamed mutex. Set to null, and we will check for null from now on.
+ name = null;
+ }
+#if !PLATFORM_UNIX
+ if (name != null && System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+#endif
+ Contract.EndContractBlock();
+
+ CreateMutexWithGuaranteedCleanup(initiallyOwned, name, out createdNew, secAttrs);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal void CreateMutexWithGuaranteedCleanup(bool initiallyOwned, String name, out bool createdNew, Win32Native.SECURITY_ATTRIBUTES secAttrs)
+ {
+ RuntimeHelpers.CleanupCode cleanupCode = new RuntimeHelpers.CleanupCode(MutexCleanupCode);
+ MutexCleanupInfo cleanupInfo = new MutexCleanupInfo(null, false);
+ MutexTryCodeHelper tryCodeHelper = new MutexTryCodeHelper(initiallyOwned, cleanupInfo, name, secAttrs, this);
+ RuntimeHelpers.TryCode tryCode = new RuntimeHelpers.TryCode(tryCodeHelper.MutexTryCode);
+ RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(
+ tryCode,
+ cleanupCode,
+ cleanupInfo);
+ createdNew = tryCodeHelper.m_newMutex;
+ }
+
+ internal class MutexTryCodeHelper
+ {
+ bool m_initiallyOwned;
+ MutexCleanupInfo m_cleanupInfo;
+ internal bool m_newMutex;
+ String m_name;
+ [System.Security.SecurityCritical] // auto-generated
+ Win32Native.SECURITY_ATTRIBUTES m_secAttrs;
+ Mutex m_mutex;
+
+ [System.Security.SecurityCritical] // auto-generated
+ [PrePrepareMethod]
+ internal MutexTryCodeHelper(bool initiallyOwned,MutexCleanupInfo cleanupInfo, String name, Win32Native.SECURITY_ATTRIBUTES secAttrs, Mutex mutex)
+ {
+ Contract.Assert(name == null || name.Length != 0);
+
+ m_initiallyOwned = initiallyOwned;
+ m_cleanupInfo = cleanupInfo;
+ m_name = name;
+ m_secAttrs = secAttrs;
+ m_mutex = mutex;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [PrePrepareMethod]
+ internal void MutexTryCode(object userData)
+ {
+ SafeWaitHandle mutexHandle = null;
+ // try block
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ }
+ finally
+ {
+ if (m_initiallyOwned)
+ {
+ m_cleanupInfo.inCriticalRegion = true;
+#if !FEATURE_CORECLR
+ Thread.BeginThreadAffinity();
+ Thread.BeginCriticalRegion();
+#endif //!FEATURE_CORECLR
+ }
+ }
+
+ int errorCode = 0;
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ }
+ finally
+ {
+ errorCode = CreateMutexHandle(m_initiallyOwned, m_name, m_secAttrs, out mutexHandle);
+ }
+
+ if (mutexHandle.IsInvalid)
+ {
+ mutexHandle.SetHandleAsInvalid();
+ if (m_name != null)
+ {
+ switch (errorCode)
+ {
+#if PLATFORM_UNIX
+ case Win32Native.ERROR_FILENAME_EXCED_RANGE:
+ // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPathComponentLength), "name");
+#endif
+
+ case Win32Native.ERROR_INVALID_HANDLE:
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", m_name));
+ }
+ }
+ __Error.WinIOError(errorCode, m_name);
+ }
+ m_newMutex = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
+ m_mutex.SetHandleInternal(mutexHandle);
+
+ m_mutex.hasThreadAffinity = true;
+
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [PrePrepareMethod]
+ private void MutexCleanupCode(Object userData, bool exceptionThrown)
+ {
+ MutexCleanupInfo cleanupInfo = (MutexCleanupInfo) userData;
+
+ // If hasThreadAffinity isn't true, we've thrown an exception in the above try, and we must free the mutex
+ // on this OS thread before ending our thread affninity.
+ if(!hasThreadAffinity) {
+ if (cleanupInfo.mutexHandle != null && !cleanupInfo.mutexHandle.IsInvalid) {
+ if( cleanupInfo.inCriticalRegion) {
+ Win32Native.ReleaseMutex(cleanupInfo.mutexHandle);
+ }
+ cleanupInfo.mutexHandle.Dispose();
+
+ }
+
+ if( cleanupInfo.inCriticalRegion) {
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+ Thread.EndThreadAffinity();
+#endif
+ }
+ }
+ }
+
+ internal class MutexCleanupInfo
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ internal SafeWaitHandle mutexHandle;
+ internal bool inCriticalRegion;
+ [System.Security.SecurityCritical] // auto-generated
+ internal MutexCleanupInfo(SafeWaitHandle mutexHandle, bool inCriticalRegion)
+ {
+ this.mutexHandle = mutexHandle;
+ this.inCriticalRegion = inCriticalRegion;
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public Mutex(bool initiallyOwned, String name) : this(initiallyOwned, name, out dummyBool) {
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public Mutex(bool initiallyOwned) : this(initiallyOwned, null, out dummyBool)
+ {
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public Mutex() : this(false, null, out dummyBool)
+ {
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private Mutex(SafeWaitHandle handle)
+ {
+ SetHandleInternal(handle);
+ hasThreadAffinity = true;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static Mutex OpenExisting(string name)
+ {
+#if !FEATURE_MACL
+ return OpenExisting(name, (MutexRights) 0);
+#else // FEATURE_MACL
+ return OpenExisting(name, MutexRights.Modify | MutexRights.Synchronize);
+#endif // FEATURE_MACL
+ }
+
+#if !FEATURE_MACL
+ public enum MutexRights
+ {
+ }
+#endif
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static Mutex OpenExisting(string name, MutexRights rights)
+ {
+ Mutex result;
+ switch (OpenExistingWorker(name, rights, out result))
+ {
+ case OpenExistingResult.NameNotFound:
+ throw new WaitHandleCannotBeOpenedException();
+
+ case OpenExistingResult.NameInvalid:
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
+
+ case OpenExistingResult.PathNotFound:
+ __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, name);
+ return result; //never executes
+
+ default:
+ return result;
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static bool TryOpenExisting(string name, out Mutex result)
+ {
+#if !FEATURE_MACL
+ return OpenExistingWorker(name, (MutexRights)0, out result) == OpenExistingResult.Success;
+#else // FEATURE_MACL
+ return OpenExistingWorker(name, MutexRights.Modify | MutexRights.Synchronize, out result) == OpenExistingResult.Success;
+#endif // FEATURE_MACL
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static bool TryOpenExisting(string name, MutexRights rights, out Mutex result)
+ {
+ return OpenExistingWorker(name, rights, out result) == OpenExistingResult.Success;
+ }
+
+ [System.Security.SecurityCritical]
+ private static OpenExistingResult OpenExistingWorker(string name, MutexRights rights, out Mutex result)
+ {
+ if (name == null)
+ {
+ throw new ArgumentNullException("name", Environment.GetResourceString("ArgumentNull_WithParamName"));
+ }
+
+ if(name.Length == 0)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "name");
+ }
+#if !PLATFORM_UNIX
+ if(System.IO.Path.MaxPath < name.Length)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+ }
+#endif
+ Contract.EndContractBlock();
+
+ result = null;
+
+ // To allow users to view & edit the ACL's, call OpenMutex
+ // with parameters to allow us to view & edit the ACL. This will
+ // fail if we don't have permission to view or edit the ACL's.
+ // If that happens, ask for less permissions.
+#if FEATURE_MACL
+ SafeWaitHandle myHandle = Win32Native.OpenMutex((int) rights, false, name);
+#else
+ SafeWaitHandle myHandle = Win32Native.OpenMutex(Win32Native.MUTEX_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
+#endif
+
+ int errorCode = 0;
+ if (myHandle.IsInvalid)
+ {
+ errorCode = Marshal.GetLastWin32Error();
+
+#if PLATFORM_UNIX
+ if (name != null && errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE)
+ {
+ // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPathComponentLength), "name");
+ }
+#endif
+
+ if(Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode)
+ return OpenExistingResult.NameNotFound;
+ if (Win32Native.ERROR_PATH_NOT_FOUND == errorCode)
+ return OpenExistingResult.PathNotFound;
+ if (null != name && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ return OpenExistingResult.NameInvalid;
+
+ // this is for passed through Win32Native Errors
+ __Error.WinIOError(errorCode,name);
+ }
+
+ result = new Mutex(myHandle);
+ return OpenExistingResult.Success;
+ }
+
+ // Note: To call ReleaseMutex, you must have an ACL granting you
+ // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value
+ // in a Mutex's ACL is MUTEX_ALL_ACCESS (0x1F0001).
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public void ReleaseMutex()
+ {
+ if (Win32Native.ReleaseMutex(safeWaitHandle))
+ {
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+ Thread.EndThreadAffinity();
+#endif
+ }
+ else
+ {
+#if FEATURE_CORECLR
+ throw new Exception(Environment.GetResourceString("Arg_SynchronizationLockException"));
+#else
+ throw new ApplicationException(Environment.GetResourceString("Arg_SynchronizationLockException"));
+#endif // FEATURE_CORECLR
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ static int CreateMutexHandle(bool initiallyOwned, String name, Win32Native.SECURITY_ATTRIBUTES securityAttribute, out SafeWaitHandle mutexHandle) {
+ int errorCode;
+ bool fAffinity = false;
+
+ while(true) {
+ mutexHandle = Win32Native.CreateMutex(securityAttribute, initiallyOwned, name);
+ errorCode = Marshal.GetLastWin32Error();
+ if( !mutexHandle.IsInvalid) {
+ break;
+ }
+
+ if( errorCode == Win32Native.ERROR_ACCESS_DENIED) {
+ // If a mutex with the name already exists, OS will try to open it with FullAccess.
+ // It might fail if we don't have enough access. In that case, we try to open the mutex will modify and synchronize access.
+ //
+
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ try
+ {
+ }
+ finally
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginThreadAffinity();
+#endif
+ fAffinity = true;
+ }
+ mutexHandle = Win32Native.OpenMutex(Win32Native.MUTEX_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
+ if(!mutexHandle.IsInvalid)
+ {
+ errorCode = Win32Native.ERROR_ALREADY_EXISTS;
+ }
+ else
+ {
+ errorCode = Marshal.GetLastWin32Error();
+ }
+ }
+ finally
+ {
+ if (fAffinity) {
+#if !FEATURE_CORECLR
+ Thread.EndThreadAffinity();
+#endif
+ }
+ }
+
+ // There could be a race condition here, the other owner of the mutex can free the mutex,
+ // We need to retry creation in that case.
+ if( errorCode != Win32Native.ERROR_FILE_NOT_FOUND) {
+ if( errorCode == Win32Native.ERROR_SUCCESS) {
+ errorCode = Win32Native.ERROR_ALREADY_EXISTS;
+ }
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ return errorCode;
+ }
+
+#if FEATURE_MACL
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public MutexSecurity GetAccessControl()
+ {
+ return new MutexSecurity(safeWaitHandle, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void SetAccessControl(MutexSecurity mutexSecurity)
+ {
+ if (mutexSecurity == null)
+ throw new ArgumentNullException("mutexSecurity");
+ Contract.EndContractBlock();
+
+ mutexSecurity.Persist(safeWaitHandle);
+ }
+#endif
+
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Overlapped.cs b/src/mscorlib/src/System/Threading/Overlapped.cs
new file mode 100644
index 0000000000..3f9fbc4989
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Overlapped.cs
@@ -0,0 +1,423 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+/*
+ * This files defines the following types:
+ * - NativeOverlapped
+ * - _IOCompletionCallback
+ * - OverlappedData
+ * - Overlapped
+ * - OverlappedDataCache
+ */
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Class for converting information to and from the native
+** overlapped structure used in asynchronous file i/o
+**
+**
+=============================================================================*/
+
+
+namespace System.Threading
+{
+ using System;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Versioning;
+ using System.Security;
+ using System.Security.Permissions;
+ using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Contracts;
+ using System.Collections.Concurrent;
+
+ #region struct NativeOverlapped
+
+ // Valuetype that represents the (unmanaged) Win32 OVERLAPPED structure
+ // the layout of this structure must be identical to OVERLAPPED.
+ // The first five matches OVERLAPPED structure.
+ // The remaining are reserved at the end
+ [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public struct NativeOverlapped
+ {
+ public IntPtr InternalLow;
+ public IntPtr InternalHigh;
+ public int OffsetLow;
+ public int OffsetHigh;
+ public IntPtr EventHandle;
+ }
+
+ #endregion struct NativeOverlapped
+
+
+ #region class _IOCompletionCallback
+
+ unsafe internal class _IOCompletionCallback
+ {
+ [System.Security.SecurityCritical] // auto-generated
+ IOCompletionCallback _ioCompletionCallback;
+ ExecutionContext _executionContext;
+ uint _errorCode; // Error code
+ uint _numBytes; // No. of bytes transferred
+ [SecurityCritical]
+ NativeOverlapped* _pOVERLAP;
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ static _IOCompletionCallback()
+ {
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback, ref StackCrawlMark stackMark)
+ {
+ _ioCompletionCallback = ioCompletionCallback;
+ // clone the exection context
+ _executionContext = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
+ }
+ // Context callback: same sig for SendOrPostCallback and ContextCallback
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #endif
+ static internal ContextCallback _ccb = new ContextCallback(IOCompletionCallback_Context);
+ [System.Security.SecurityCritical]
+ static internal void IOCompletionCallback_Context(Object state)
+ {
+ _IOCompletionCallback helper = (_IOCompletionCallback)state;
+ Contract.Assert(helper != null,"_IOCompletionCallback cannot be null");
+ helper._ioCompletionCallback(helper._errorCode, helper._numBytes, helper._pOVERLAP);
+ }
+
+
+ // call back helper
+ [System.Security.SecurityCritical] // auto-generated
+ static unsafe internal void PerformIOCompletionCallback(uint errorCode, // Error code
+ uint numBytes, // No. of bytes transferred
+ NativeOverlapped* pOVERLAP // ptr to OVERLAP structure
+ )
+ {
+ Overlapped overlapped;
+ _IOCompletionCallback helper;
+
+ do
+ {
+ overlapped = OverlappedData.GetOverlappedFromNative(pOVERLAP).m_overlapped;
+ helper = overlapped.iocbHelper;
+
+ if (helper == null || helper._executionContext == null || helper._executionContext.IsDefaultFTContext(true))
+ {
+ // We got here because of UnsafePack (or) Pack with EC flow supressed
+ IOCompletionCallback callback = overlapped.UserCallback;
+ callback( errorCode, numBytes, pOVERLAP);
+ }
+ else
+ {
+ // We got here because of Pack
+ helper._errorCode = errorCode;
+ helper._numBytes = numBytes;
+ helper._pOVERLAP = pOVERLAP;
+ using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
+ ExecutionContext.Run(executionContext, _ccb, helper, true);
+ }
+
+ //Quickly check the VM again, to see if a packet has arrived.
+
+ OverlappedData.CheckVMForIOPacket(out pOVERLAP, out errorCode, out numBytes);
+
+ } while (pOVERLAP != null);
+
+ }
+ }
+
+ #endregion class _IOCompletionCallback
+
+
+ #region class OverlappedData
+
+ sealed internal class OverlappedData
+ {
+ // ! If you make any change to the layout here, you need to make matching change
+ // ! to OverlappedObject in vm\nativeoverlapped.h
+ internal IAsyncResult m_asyncResult;
+ [System.Security.SecurityCritical] // auto-generated
+ internal IOCompletionCallback m_iocb;
+ internal _IOCompletionCallback m_iocbHelper;
+ internal Overlapped m_overlapped;
+ private Object m_userObject;
+ private IntPtr m_pinSelf;
+ private IntPtr m_userObjectInternal;
+ private int m_AppDomainId;
+#pragma warning disable 414 // Field is not used from managed.
+#pragma warning disable 169
+ private byte m_isArray;
+ private byte m_toBeCleaned;
+#pragma warning restore 414
+#pragma warning restore 169
+ internal NativeOverlapped m_nativeOverlapped;
+
+#if FEATURE_CORECLR
+ // Adding an empty default ctor for annotation purposes
+ [System.Security.SecuritySafeCritical] // auto-generated
+ internal OverlappedData(){}
+#endif // FEATURE_CORECLR
+
+
+ [System.Security.SecurityCritical]
+ internal void ReInitialize()
+ {
+ m_asyncResult = null;
+ m_iocb = null;
+ m_iocbHelper = null;
+ m_overlapped = null;
+ m_userObject = null;
+ Contract.Assert(m_pinSelf.IsNull(), "OverlappedData has not been freed: m_pinSelf");
+ m_pinSelf = (IntPtr)0;
+ m_userObjectInternal = (IntPtr)0;
+ Contract.Assert(m_AppDomainId == 0 || m_AppDomainId == AppDomain.CurrentDomain.Id, "OverlappedData is not in the current domain");
+ m_AppDomainId = 0;
+ m_nativeOverlapped.EventHandle = (IntPtr)0;
+ m_isArray = 0;
+ m_nativeOverlapped.InternalLow = (IntPtr)0;
+ m_nativeOverlapped.InternalHigh = (IntPtr)0;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ unsafe internal NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData)
+ {
+ if (!m_pinSelf.IsNull()) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_Overlapped_Pack"));
+ }
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+
+ if (iocb != null)
+ {
+ m_iocbHelper = new _IOCompletionCallback(iocb, ref stackMark);
+ m_iocb = iocb;
+ }
+ else
+ {
+ m_iocbHelper = null;
+ m_iocb = null;
+ }
+ m_userObject = userData;
+ if (m_userObject != null)
+ {
+ if (m_userObject.GetType() == typeof(Object[]))
+ {
+ m_isArray = 1;
+ }
+ else
+ {
+ m_isArray = 0;
+ }
+ }
+ return AllocateNativeOverlapped();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ unsafe internal NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData)
+ {
+ if (!m_pinSelf.IsNull()) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_Overlapped_Pack"));
+ }
+ m_userObject = userData;
+ if (m_userObject != null)
+ {
+ if (m_userObject.GetType() == typeof(Object[]))
+ {
+ m_isArray = 1;
+ }
+ else
+ {
+ m_isArray = 0;
+ }
+ }
+ m_iocb = iocb;
+ m_iocbHelper = null;
+ return AllocateNativeOverlapped();
+ }
+
+ [ComVisible(false)]
+ internal IntPtr UserHandle
+ {
+ get { return m_nativeOverlapped.EventHandle; }
+ set { m_nativeOverlapped.EventHandle = value; }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ unsafe private extern NativeOverlapped* AllocateNativeOverlapped();
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ unsafe internal static extern void FreeNativeOverlapped(NativeOverlapped* nativeOverlappedPtr);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ unsafe internal static extern OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ unsafe internal static extern void CheckVMForIOPacket(out NativeOverlapped* pOVERLAP, out uint errorCode, out uint numBytes);
+ }
+
+ #endregion class OverlappedData
+
+
+ #region class Overlapped
+
+ /// <internalonly/>
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class Overlapped
+ {
+ private OverlappedData m_overlappedData;
+ private static PinnableBufferCache s_overlappedDataCache = new PinnableBufferCache("System.Threading.OverlappedData", ()=> new OverlappedData());
+
+#if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+#endif
+ public Overlapped()
+ {
+ m_overlappedData = (OverlappedData) s_overlappedDataCache.Allocate();
+ m_overlappedData.m_overlapped = this;
+ }
+
+ public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult ar)
+ {
+ m_overlappedData = (OverlappedData) s_overlappedDataCache.Allocate();
+ m_overlappedData.m_overlapped = this;
+ m_overlappedData.m_nativeOverlapped.OffsetLow = offsetLo;
+ m_overlappedData.m_nativeOverlapped.OffsetHigh = offsetHi;
+ m_overlappedData.UserHandle = hEvent;
+ m_overlappedData.m_asyncResult = ar;
+ }
+
+ [Obsolete("This constructor is not 64-bit compatible. Use the constructor that takes an IntPtr for the event handle. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public Overlapped(int offsetLo, int offsetHi, int hEvent, IAsyncResult ar) : this(offsetLo, offsetHi, new IntPtr(hEvent), ar)
+ {
+ }
+
+ public IAsyncResult AsyncResult
+ {
+ get { return m_overlappedData.m_asyncResult; }
+ set { m_overlappedData.m_asyncResult = value; }
+ }
+
+ public int OffsetLow
+ {
+ get { return m_overlappedData.m_nativeOverlapped.OffsetLow; }
+ set { m_overlappedData.m_nativeOverlapped.OffsetLow = value; }
+ }
+
+ public int OffsetHigh
+ {
+ get { return m_overlappedData.m_nativeOverlapped.OffsetHigh; }
+ set { m_overlappedData.m_nativeOverlapped.OffsetHigh = value; }
+ }
+
+ [Obsolete("This property is not 64-bit compatible. Use EventHandleIntPtr instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public int EventHandle
+ {
+ get { return m_overlappedData.UserHandle.ToInt32(); }
+ set { m_overlappedData.UserHandle = new IntPtr(value); }
+ }
+
+ [ComVisible(false)]
+ public IntPtr EventHandleIntPtr
+ {
+ get { return m_overlappedData.UserHandle; }
+ set { m_overlappedData.UserHandle = value; }
+ }
+
+ internal _IOCompletionCallback iocbHelper
+ {
+ get { return m_overlappedData.m_iocbHelper; }
+ }
+
+ internal IOCompletionCallback UserCallback
+ {
+ [System.Security.SecurityCritical]
+ get { return m_overlappedData.m_iocb; }
+ }
+
+ /*====================================================================
+ * Packs a managed overlapped class into native Overlapped struct.
+ * Roots the iocb and stores it in the ReservedCOR field of native Overlapped
+ * Pins the native Overlapped struct and returns the pinned index.
+ ====================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [Obsolete("This method is not safe. Use Pack (iocb, userData) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ [CLSCompliant(false)]
+ unsafe public NativeOverlapped* Pack(IOCompletionCallback iocb)
+ {
+ return Pack (iocb, null);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [CLSCompliant(false),ComVisible(false)]
+ unsafe public NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData)
+ {
+ return m_overlappedData.Pack(iocb, userData);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [Obsolete("This method is not safe. Use UnsafePack (iocb, userData) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ [CLSCompliant(false)]
+ unsafe public NativeOverlapped* UnsafePack(IOCompletionCallback iocb)
+ {
+ return UnsafePack (iocb, null);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [CLSCompliant(false), ComVisible(false)]
+ unsafe public NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData)
+ {
+ return m_overlappedData.UnsafePack(iocb, userData);
+ }
+
+ /*====================================================================
+ * Unpacks an unmanaged native Overlapped struct.
+ * Unpins the native Overlapped struct
+ ====================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [CLSCompliant(false)]
+ unsafe public static Overlapped Unpack(NativeOverlapped* nativeOverlappedPtr)
+ {
+ if (nativeOverlappedPtr == null)
+ throw new ArgumentNullException("nativeOverlappedPtr");
+ Contract.EndContractBlock();
+
+ Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped;
+
+ return overlapped;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [CLSCompliant(false)]
+ unsafe public static void Free(NativeOverlapped* nativeOverlappedPtr)
+ {
+ if (nativeOverlappedPtr == null)
+ throw new ArgumentNullException("nativeOverlappedPtr");
+ Contract.EndContractBlock();
+
+ Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped;
+ OverlappedData.FreeNativeOverlapped(nativeOverlappedPtr);
+ OverlappedData overlappedData = overlapped.m_overlappedData;
+ overlapped.m_overlappedData = null;
+ overlappedData.ReInitialize();
+ s_overlappedDataCache.Free(overlappedData);
+ }
+
+ }
+
+ #endregion class Overlapped
+
+} // namespace
diff --git a/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs b/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs
new file mode 100644
index 0000000000..45d24fef49
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: This class is a Delegate which defines the start method
+** for starting a thread. That method must match this delegate.
+**
+**
+=============================================================================*/
+
+
+namespace System.Threading {
+ using System.Security.Permissions;
+ using System.Threading;
+ using System.Runtime.InteropServices;
+
+ [ComVisibleAttribute(false)]
+ public delegate void ParameterizedThreadStart(object obj);
+}
diff --git a/src/mscorlib/src/System/Threading/ReaderWriterLock.cs b/src/mscorlib/src/System/Threading/ReaderWriterLock.cs
new file mode 100644
index 0000000000..8cead1a87a
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ReaderWriterLock.cs
@@ -0,0 +1,306 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*============================================================
+**
+**
+**
+** Purpose: Defines the lock that implements
+** single-writer/multiple-reader semantics
+**
+**
+===========================================================*/
+
+#if FEATURE_RWLOCK
+namespace System.Threading {
+ using System.Threading;
+ using System.Security.Permissions;
+ using System.Runtime.Remoting;
+ using System;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [ComVisible(true)]
+ public sealed class ReaderWriterLock: CriticalFinalizerObject
+ {
+ /*
+ * Constructor
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public ReaderWriterLock()
+ {
+ PrivateInitialize();
+ }
+
+ /*
+ * Destructor
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ ~ReaderWriterLock()
+ {
+ PrivateDestruct();
+ }
+
+ /*
+ * Property that returns TRUE if the reader lock is held
+ * by the current thread
+ */
+ public bool IsReaderLockHeld {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get {
+ return(PrivateGetIsReaderLockHeld());
+ }
+ }
+
+ /*
+ * Property that returns TRUE if the writer lock is held
+ * by the current thread
+ */
+ public bool IsWriterLockHeld {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get {
+ return(PrivateGetIsWriterLockHeld());
+ }
+ }
+
+ /*
+ * Property that returns the current writer sequence number.
+ * The caller should be a reader or writer for getting
+ * meaningful results
+ */
+ public int WriterSeqNum {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ return(PrivateGetWriterSeqNum());
+ }
+ }
+
+ /*
+ * Acquires reader lock. The thread will block if a different
+ * thread has writer lock.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void AcquireReaderLockInternal(int millisecondsTimeout);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void AcquireReaderLock(int millisecondsTimeout)
+ {
+ AcquireReaderLockInternal(millisecondsTimeout);
+ }
+
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void AcquireReaderLock(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ AcquireReaderLockInternal((int)tm);
+ }
+
+ /*
+ * Acquires writer lock. The thread will block if a different
+ * thread has reader lock. It will dead lock if this thread
+ * has reader lock. Use UpgardeToWriterLock when you are not
+ * sure if the thread has reader lock
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void AcquireWriterLockInternal(int millisecondsTimeout);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void AcquireWriterLock(int millisecondsTimeout)
+ {
+ AcquireWriterLockInternal(millisecondsTimeout);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void AcquireWriterLock(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ AcquireWriterLockInternal((int)tm);
+ }
+
+
+ /*
+ * Releases reader lock.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private extern void ReleaseReaderLockInternal();
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public void ReleaseReaderLock()
+ {
+ ReleaseReaderLockInternal();
+ }
+
+ /*
+ * Releases writer lock.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private extern void ReleaseWriterLockInternal();
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public void ReleaseWriterLock()
+ {
+ ReleaseWriterLockInternal();
+ }
+
+ /*
+ * Upgardes the thread to a writer. If the thread has is a
+ * reader, it is possible that the reader lock was
+ * released before writer lock was acquired.
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public LockCookie UpgradeToWriterLock(int millisecondsTimeout)
+ {
+ LockCookie result = new LockCookie ();
+ FCallUpgradeToWriterLock (ref result, millisecondsTimeout);
+ return result;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void FCallUpgradeToWriterLock(ref LockCookie result, int millisecondsTimeout);
+
+ public LockCookie UpgradeToWriterLock(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ return UpgradeToWriterLock((int)tm);
+ }
+
+ /*
+ * Restores the lock status of the thread to the one it was
+ * in when it called UpgradeToWriterLock.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void DowngradeFromWriterLockInternal(ref LockCookie lockCookie);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void DowngradeFromWriterLock(ref LockCookie lockCookie)
+ {
+ DowngradeFromWriterLockInternal(ref lockCookie);
+ }
+
+ /*
+ * Releases the lock irrespective of the number of times the thread
+ * acquired the lock
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public LockCookie ReleaseLock()
+ {
+ LockCookie result = new LockCookie ();
+ FCallReleaseLock (ref result);
+ return result;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void FCallReleaseLock(ref LockCookie result);
+
+ /*
+ * Restores the lock status of the thread to the one it was
+ * in when it called ReleaseLock.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void RestoreLockInternal(ref LockCookie lockCookie);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void RestoreLock(ref LockCookie lockCookie)
+ {
+ RestoreLockInternal(ref lockCookie);
+ }
+
+ /*
+ * Internal helper that returns TRUE if the reader lock is held
+ * by the current thread
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private extern bool PrivateGetIsReaderLockHeld();
+
+ /*
+ * Internal helper that returns TRUE if the writer lock is held
+ * by the current thread
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private extern bool PrivateGetIsWriterLockHeld();
+
+ /*
+ * Internal helper that returns the current writer sequence
+ * number. The caller should be a reader or writer for getting
+ * meaningful results
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern int PrivateGetWriterSeqNum();
+
+ /*
+ * Returns true if there were intermediate writes since the
+ * sequence number was obtained. The caller should be
+ * a reader or writer for getting meaningful results
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public extern bool AnyWritersSince(int seqNum);
+
+ // Initialize state kept inside the lock
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void PrivateInitialize();
+
+ // Destruct resource associated with the lock
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void PrivateDestruct();
+
+ // State
+#pragma warning disable 169
+#pragma warning disable 414 // These fields are not used from managed.
+ private IntPtr _hWriterEvent;
+ private IntPtr _hReaderEvent;
+ private IntPtr _hObjectHandle;
+ private int _dwState = 0;
+ private int _dwULockID = 0;
+ private int _dwLLockID = 0;
+ private int _dwWriterID = 0;
+ private int _dwWriterSeqNum = 0;
+ private short _wWriterLevel;
+#if RWLOCK_STATISTICS
+ // WARNING: You must explicitly #define RWLOCK_STATISTICS when you
+ // build in both the VM and BCL directories if you want this.
+ private int _dwReaderEntryCount = 0;
+ private int _dwReaderContentionCount = 0;
+ private int _dwWriterEntryCount = 0;
+ private int _dwWriterContentionCount = 0;
+ private int _dwEventsReleasedCount = 0;
+#endif // RWLOCK_STATISTICS
+#pragma warning restore 414
+#pragma warning restore 169
+ }
+}
+#endif //FEATURE_RWLOCK
diff --git a/src/mscorlib/src/System/Threading/Semaphore.cs b/src/mscorlib/src/System/Threading/Semaphore.cs
new file mode 100644
index 0000000000..303593b776
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Semaphore.cs
@@ -0,0 +1,201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace System.Threading
+{
+ public sealed partial class Semaphore : WaitHandle
+ {
+ [SecuritySafeCritical]
+ public Semaphore(int initialCount, int maximumCount) : this(initialCount, maximumCount, null) { }
+
+ [SecurityCritical]
+ public Semaphore(int initialCount, int maximumCount, string name)
+ {
+ if (initialCount < 0)
+ {
+ throw new ArgumentOutOfRangeException("initialCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (maximumCount < 1)
+ {
+ throw new ArgumentOutOfRangeException("maximumCount", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
+ }
+
+ if (initialCount > maximumCount)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_SemaphoreInitialMaximum"));
+ }
+
+ SafeWaitHandle myHandle = CreateSemaphone(initialCount, maximumCount, name);
+
+ if (myHandle.IsInvalid)
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ throw new WaitHandleCannotBeOpenedException(
+ Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
+
+ __Error.WinIOError();
+ }
+ this.SafeWaitHandle = myHandle;
+ }
+
+ [SecurityCritical]
+ public Semaphore(int initialCount, int maximumCount, string name, out bool createdNew)
+ {
+ if (initialCount < 0)
+ {
+ throw new ArgumentOutOfRangeException("initialCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (maximumCount < 1)
+ {
+ throw new ArgumentOutOfRangeException("maximumCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (initialCount > maximumCount)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_SemaphoreInitialMaximum"));
+ }
+
+ SafeWaitHandle myHandle = CreateSemaphone(initialCount, maximumCount, name);
+
+ int errorCode = Marshal.GetLastWin32Error();
+ if (myHandle.IsInvalid)
+ {
+ if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ throw new WaitHandleCannotBeOpenedException(
+ Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
+ __Error.WinIOError();
+ }
+ createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
+ this.SafeWaitHandle = myHandle;
+ }
+
+ [SecurityCritical]
+ private Semaphore(SafeWaitHandle handle)
+ {
+ this.SafeWaitHandle = handle;
+ }
+
+ [SecurityCritical]
+ private static SafeWaitHandle CreateSemaphone(int initialCount, int maximumCount, string name)
+ {
+ if (name != null)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives"));
+#else
+ if (name.Length > Path.MaxPath)
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+#endif
+ }
+
+ Contract.Assert(initialCount >= 0);
+ Contract.Assert(maximumCount >= 1);
+ Contract.Assert(initialCount <= maximumCount);
+
+ return Win32Native.CreateSemaphore(null, initialCount, maximumCount, name);
+ }
+
+ [SecurityCritical]
+
+ public static Semaphore OpenExisting(string name)
+ {
+ Semaphore result;
+ switch (OpenExistingWorker(name, out result))
+ {
+ case OpenExistingResult.NameNotFound:
+ throw new WaitHandleCannotBeOpenedException();
+ case OpenExistingResult.NameInvalid:
+ throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
+ case OpenExistingResult.PathNotFound:
+ throw new IOException(Win32Native.GetMessage(Win32Native.ERROR_PATH_NOT_FOUND));
+ default:
+ return result;
+ }
+ }
+
+ [SecurityCritical]
+ public static bool TryOpenExisting(string name, out Semaphore result)
+ {
+ return OpenExistingWorker(name, out result) == OpenExistingResult.Success;
+ }
+
+ [SecurityCritical]
+ private static OpenExistingResult OpenExistingWorker(string name, out Semaphore result)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives"));
+#else
+ if (name == null)
+ throw new ArgumentNullException("name", Environment.GetResourceString("ArgumentNull_WithParamName"));
+ if (name.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "name");
+ if (name.Length > Path.MaxPath)
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), "name");
+
+ const int SYNCHRONIZE = 0x00100000;
+ const int SEMAPHORE_MODIFY_STATE = 0x00000002;
+
+ //Pass false to OpenSemaphore to prevent inheritedHandles
+ SafeWaitHandle myHandle = Win32Native.OpenSemaphore(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, false, name);
+
+ if (myHandle.IsInvalid)
+ {
+ result = null;
+
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode)
+ return OpenExistingResult.NameNotFound;
+ if (Win32Native.ERROR_PATH_NOT_FOUND == errorCode)
+ return OpenExistingResult.PathNotFound;
+ if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
+ return OpenExistingResult.NameInvalid;
+ //this is for passed through NativeMethods Errors
+ __Error.WinIOError();
+ }
+
+ result = new Semaphore(myHandle);
+ return OpenExistingResult.Success;
+#endif
+ }
+
+ public int Release()
+ {
+ return Release(1);
+ }
+
+ // increase the count on a semaphore, returns previous count
+ [SecuritySafeCritical]
+ public int Release(int releaseCount)
+ {
+ if (releaseCount < 1)
+ {
+ throw new ArgumentOutOfRangeException("releaseCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ //If ReleaseSempahore returns false when the specified value would cause
+ // the semaphore's count to exceed the maximum count set when Semaphore was created
+ //Non-Zero return
+
+ int previousCount;
+ if (!Win32Native.ReleaseSemaphore(SafeWaitHandle, releaseCount, out previousCount))
+ {
+ throw new SemaphoreFullException();
+ }
+
+ return previousCount;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/SemaphoreFullException.cs b/src/mscorlib/src/System/Threading/SemaphoreFullException.cs
new file mode 100644
index 0000000000..e1928e05de
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SemaphoreFullException.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading {
+ using System;
+ using System.Runtime.Serialization;
+ using System.Runtime.InteropServices;
+
+ [Serializable]
+ [ComVisibleAttribute(false)]
+#if !FEATURE_CORECLR
+ [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
+#endif
+ public class SemaphoreFullException : SystemException {
+
+ public SemaphoreFullException() : base(Environment.GetResourceString("Threading_SemaphoreFullException")){
+ }
+
+ public SemaphoreFullException(String message) : base(message) {
+ }
+
+ public SemaphoreFullException(String message, Exception innerException) : base(message, innerException) {
+ }
+
+ protected SemaphoreFullException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs
new file mode 100644
index 0000000000..c2dcbb3451
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs
@@ -0,0 +1,930 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A lightweight semahore class that contains the basic semaphore functions plus some useful functions like interrupt
+// and wait handle exposing to allow waiting on multiple semaphores.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.InteropServices;
+using System.Diagnostics.Contracts;
+using System.Threading.Tasks;
+
+// The class will be part of the current System.Threading namespace
+namespace System.Threading
+{
+ /// <summary>
+ /// Limits the number of threads that can access a resource or pool of resources concurrently.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="SemaphoreSlim"/> provides a lightweight semaphore class that doesn't
+ /// use Windows kernel semaphores.
+ /// </para>
+ /// <para>
+ /// All public and protected members of <see cref="SemaphoreSlim"/> are thread-safe and may be used
+ /// concurrently from multiple threads, with the exception of Dispose, which
+ /// must only be used when all other operations on the <see cref="SemaphoreSlim"/> have
+ /// completed.
+ /// </para>
+ /// </remarks>
+ [ComVisible(false)]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerDisplay("Current Count = {m_currentCount}")]
+ public class SemaphoreSlim : IDisposable
+ {
+ #region Private Fields
+
+ // The semaphore count, initialized in the constructor to the initial value, every release call incremetns it
+ // and every wait call decrements it as long as its value is positive otherwise the wait will block.
+ // Its value must be between the maximum semaphore value and zero
+ private volatile int m_currentCount;
+
+ // The maximum semaphore value, it is initialized to Int.MaxValue if the client didn't specify it. it is used
+ // to check if the count excceeded the maxi value or not.
+ private readonly int m_maxCount;
+
+ // The number of synchronously waiting threads, it is set to zero in the constructor and increments before blocking the
+ // threading and decrements it back after that. It is used as flag for the release call to know if there are
+ // waiting threads in the monitor or not.
+ private volatile int m_waitCount;
+
+ // Dummy object used to in lock statements to protect the semaphore count, wait handle and cancelation
+ private object m_lockObj;
+
+ // Act as the semaphore wait handle, it's lazily initialized if needed, the first WaitHandle call initialize it
+ // and wait an release sets and resets it respectively as long as it is not null
+ private volatile ManualResetEvent m_waitHandle;
+
+ // Head of list representing asynchronous waits on the semaphore.
+ private TaskNode m_asyncHead;
+
+ // Tail of list representing asynchronous waits on the semaphore.
+ private TaskNode m_asyncTail;
+
+ // A pre-completed task with Result==true
+ private readonly static Task<bool> s_trueTask =
+ new Task<bool>(false, true, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));
+ // A pre-completed task with Result==false
+ private readonly static Task<bool> s_falseTask =
+ new Task<bool>(false, false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));
+
+ // No maximum constant
+ private const int NO_MAXIMUM = Int32.MaxValue;
+
+ // Task in a linked list of asynchronous waiters
+ private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem
+ {
+ internal TaskNode Prev, Next;
+ internal TaskNode() : base() {}
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+ bool setSuccessfully = TrySetResult(true);
+ Contract.Assert(setSuccessfully, "Should have been able to complete task");
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
+ }
+ #endregion
+
+ #region Public properties
+
+ /// <summary>
+ /// Gets the current count of the <see cref="SemaphoreSlim"/>.
+ /// </summary>
+ /// <value>The current count of the <see cref="SemaphoreSlim"/>.</value>
+ public int CurrentCount
+ {
+ get { return m_currentCount; }
+ }
+
+ /// <summary>
+ /// Returns a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the semaphore.
+ /// </summary>
+ /// <value>A <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the
+ /// semaphore.</value>
+ /// <remarks>
+ /// A successful wait on the <see cref="AvailableWaitHandle"/> does not imply a successful wait on
+ /// the <see cref="SemaphoreSlim"/> itself, nor does it decrement the semaphore's
+ /// count. <see cref="AvailableWaitHandle"/> exists to allow a thread to block waiting on multiple
+ /// semaphores, but such a wait should be followed by a true wait on the target semaphore.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The <see
+ /// cref="SemaphoreSlim"/> has been disposed.</exception>
+ public WaitHandle AvailableWaitHandle
+ {
+ get
+ {
+ CheckDispose();
+
+ // Return it directly if it is not null
+ if (m_waitHandle != null)
+ return m_waitHandle;
+
+ //lock the count to avoid multiple threads initializing the handle if it is null
+ lock (m_lockObj)
+ {
+ if (m_waitHandle == null)
+ {
+ // The initial state for the wait handle is true if the count is greater than zero
+ // false otherwise
+ m_waitHandle = new ManualResetEvent(m_currentCount != 0);
+ }
+ }
+ return m_waitHandle;
+ }
+ }
+
+ #endregion
+
+ #region Constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
+ /// the initial number of requests that can be granted concurrently.
+ /// </summary>
+ /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
+ /// concurrently.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/>
+ /// is less than 0.</exception>
+ public SemaphoreSlim(int initialCount)
+ : this(initialCount, NO_MAXIMUM)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
+ /// the initial and maximum number of requests that can be granted concurrently.
+ /// </summary>
+ /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
+ /// concurrently.</param>
+ /// <param name="maxCount">The maximum number of requests for the semaphore that can be granted
+ /// concurrently.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="initialCount"/>
+ /// is less than 0. -or-
+ /// <paramref name="initialCount"/> is greater than <paramref name="maxCount"/>. -or-
+ /// <paramref name="maxCount"/> is less than 0.</exception>
+ public SemaphoreSlim(int initialCount, int maxCount)
+ {
+ if (initialCount < 0 || initialCount > maxCount)
+ {
+ throw new ArgumentOutOfRangeException(
+ "initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong"));
+ }
+
+ //validate input
+ if (maxCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong"));
+ }
+
+ m_maxCount = maxCount;
+ m_lockObj = new object();
+ m_currentCount = initialCount;
+ }
+
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>.
+ /// </summary>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public void Wait()
+ {
+ // Call wait with infinite timeout
+ Wait(Timeout.Infinite, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, while observing a
+ /// <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> token to
+ /// observe.</param>
+ /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> was
+ /// canceled.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public void Wait(CancellationToken cancellationToken)
+ {
+ // Call wait with infinite timeout
+ Wait(Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
+ /// cref="T:System.TimeSpan"/> to measure the time interval.
+ /// </summary>
+ /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+ /// otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ public bool Wait(TimeSpan timeout)
+ {
+ // Validate the timeout
+ Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new System.ArgumentOutOfRangeException(
+ "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
+ }
+
+ // Call wait with the timeout milliseconds
+ return Wait((int)timeout.TotalMilliseconds, new CancellationToken());
+ }
+
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
+ /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a <see
+ /// cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+ /// observe.</param>
+ /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+ /// otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/>.</exception>
+ /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
+ {
+ // Validate the timeout
+ Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new System.ArgumentOutOfRangeException(
+ "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
+ }
+
+ // Call wait with the timeout milliseconds
+ return Wait((int)timeout.TotalMilliseconds, cancellationToken);
+ }
+
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a 32-bit
+ /// signed integer to measure the time interval.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+ /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+ /// otherwise, false.</returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ public bool Wait(int millisecondsTimeout)
+ {
+ return Wait(millisecondsTimeout, new CancellationToken());
+ }
+
+
+ /// <summary>
+ /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>,
+ /// using a 32-bit signed integer to measure the time interval,
+ /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to
+ /// wait indefinitely.</param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
+ /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+ /// which represents an infinite time-out.</exception>
+ /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
+ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ CheckDispose();
+
+ // Validate input
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException(
+ "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Perf: Check the stack timeout parameter before checking the volatile count
+ if (millisecondsTimeout == 0 && m_currentCount == 0)
+ {
+ // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!)
+ return false;
+ }
+
+ uint startTime = 0;
+ if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
+ {
+ startTime = TimeoutHelper.GetTime();
+ }
+
+ bool waitSuccessful = false;
+ Task<bool> asyncWaitTask = null;
+ bool lockTaken = false;
+
+ //Register for cancellation outside of the main lock.
+ //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could
+ // occur for (1)this.m_lockObj and (2)cts.internalLock
+ CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
+ try
+ {
+ // Perf: first spin wait for the count to be positive, but only up to the first planned yield.
+ // This additional amount of spinwaiting in addition
+ // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios.
+ //
+ SpinWait spin = new SpinWait();
+ while (m_currentCount == 0 && !spin.NextSpinWillYield)
+ {
+ spin.SpinOnce();
+ }
+ // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot
+ // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters.
+ try { }
+ finally
+ {
+ Monitor.Enter(m_lockObj, ref lockTaken);
+ if (lockTaken)
+ {
+ m_waitCount++;
+ }
+ }
+
+ // If there are any async waiters, for fairness we'll get in line behind
+ // then by translating our synchronous wait into an asynchronous one that we
+ // then block on (once we've released the lock).
+ if (m_asyncHead != null)
+ {
+ Contract.Assert(m_asyncTail != null, "tail should not be null if head isn't");
+ asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken);
+ }
+ // There are no async waiters, so we can proceed with normal synchronous waiting.
+ else
+ {
+ // If the count > 0 we are good to move on.
+ // If not, then wait if we were given allowed some wait duration
+
+ OperationCanceledException oce = null;
+
+ if (m_currentCount == 0)
+ {
+ if (millisecondsTimeout == 0)
+ {
+ return false;
+ }
+
+ // Prepare for the main wait...
+ // wait until the count become greater than zero or the timeout is expired
+ try
+ {
+ waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken);
+ }
+ catch (OperationCanceledException e) { oce = e; }
+ }
+
+ // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't
+ // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters
+ // defer to synchronous waiters in priority, which means that if it's possible an asynchronous
+ // waiter didn't get released because a synchronous waiter was present, we need to ensure
+ // that synchronous waiter succeeds so that they have a chance to release.
+ Contract.Assert(!waitSuccessful || m_currentCount > 0,
+ "If the wait was successful, there should be count available.");
+ if (m_currentCount > 0)
+ {
+ waitSuccessful = true;
+ m_currentCount--;
+ }
+ else if (oce != null)
+ {
+ throw oce;
+ }
+
+ // Exposing wait handle which is lazily initialized if needed
+ if (m_waitHandle != null && m_currentCount == 0)
+ {
+ m_waitHandle.Reset();
+ }
+ }
+ }
+ finally
+ {
+ // Release the lock
+ if (lockTaken)
+ {
+ m_waitCount--;
+ Monitor.Exit(m_lockObj);
+ }
+
+ // Unregister the cancellation callback.
+ cancellationTokenRegistration.Dispose();
+ }
+
+ // If we had to fall back to asynchronous waiting, block on it
+ // here now that we've released the lock, and return its
+ // result when available. Otherwise, this was a synchronous
+ // wait, and whether we successfully acquired the semaphore is
+ // stored in waitSuccessful.
+
+ return (asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful;
+ }
+
+ /// <summary>
+ /// Local helper function, waits on the monitor until the monitor recieves signal or the
+ /// timeout is expired
+ /// </summary>
+ /// <param name="millisecondsTimeout">The maximum timeout</param>
+ /// <param name="startTime">The start ticks to calculate the elapsed time</param>
+ /// <param name="cancellationToken">The CancellationToken to observe.</param>
+ /// <returns>true if the monitor recieved a signal, false if the timeout expired</returns>
+ private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
+ {
+ int remainingWaitMilliseconds = Timeout.Infinite;
+
+ //Wait on the monitor as long as the count is zero
+ while (m_currentCount == 0)
+ {
+ // If cancelled, we throw. Trying to wait could lead to deadlock.
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (millisecondsTimeout != Timeout.Infinite)
+ {
+ remainingWaitMilliseconds = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
+ if (remainingWaitMilliseconds <= 0)
+ {
+ // The thread has expires its timeout
+ return false;
+ }
+ }
+ // ** the actual wait **
+ if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>.
+ /// </summary>
+ /// <returns>A task that will complete when the semaphore has been entered.</returns>
+ public Task WaitAsync()
+ {
+ return WaitAsync(Timeout.Infinite, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, while observing a
+ /// <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <returns>A task that will complete when the semaphore has been entered.</returns>
+ /// <param name="cancellationToken">
+ /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
+ /// </param>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The current instance has already been disposed.
+ /// </exception>
+ public Task WaitAsync(CancellationToken cancellationToken)
+ {
+ return WaitAsync(Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
+ /// using a 32-bit signed integer to measure the time interval.
+ /// </summary>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
+ /// </param>
+ /// <returns>
+ /// A task that will complete with a result of true if the current thread successfully entered
+ /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+ /// </returns>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+ /// which represents an infinite time-out.
+ /// </exception>
+ public Task<bool> WaitAsync(int millisecondsTimeout)
+ {
+ return WaitAsync(millisecondsTimeout, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
+ /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a
+ /// <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="timeout">
+ /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
+ /// </param>
+ /// <returns>
+ /// A task that will complete with a result of true if the current thread successfully entered
+ /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+ /// </returns>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The current instance has already been disposed.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents
+ /// an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+ /// </exception>
+ public Task<bool> WaitAsync(TimeSpan timeout)
+ {
+ return WaitAsync(timeout, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
+ /// cref="T:System.TimeSpan"/> to measure the time interval.
+ /// </summary>
+ /// <param name="timeout">
+ /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <returns>
+ /// A task that will complete with a result of true if the current thread successfully entered
+ /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents
+ /// an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+ /// </exception>
+ public Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
+ {
+ // Validate the timeout
+ Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new System.ArgumentOutOfRangeException(
+ "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
+ }
+
+ // Call wait with the timeout milliseconds
+ return WaitAsync((int)timeout.TotalMilliseconds, cancellationToken);
+ }
+
+ /// <summary>
+ /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
+ /// using a 32-bit signed integer to measure the time interval,
+ /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
+ /// </summary>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
+ /// <returns>
+ /// A task that will complete with a result of true if the current thread successfully entered
+ /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+ /// </returns>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+ /// which represents an infinite time-out.
+ /// </exception>
+ public Task<bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ CheckDispose();
+
+ // Validate input
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException(
+ "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
+ }
+
+ // Bail early for cancellation
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled<bool>(cancellationToken);
+
+ lock (m_lockObj)
+ {
+ // If there are counts available, allow this waiter to succeed.
+ if (m_currentCount > 0)
+ {
+ --m_currentCount;
+ if (m_waitHandle != null && m_currentCount == 0) m_waitHandle.Reset();
+ return s_trueTask;
+ }
+ else if (millisecondsTimeout == 0)
+ {
+ // No counts, if timeout is zero fail fast
+ return s_falseTask;
+ }
+ // If there aren't, create and return a task to the caller.
+ // The task will be completed either when they've successfully acquired
+ // the semaphore or when the timeout expired or cancellation was requested.
+ else
+ {
+ Contract.Assert(m_currentCount == 0, "m_currentCount should never be negative");
+ var asyncWaiter = CreateAndAddAsyncWaiter();
+ return (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled) ?
+ asyncWaiter :
+ WaitUntilCountOrTimeoutAsync(asyncWaiter, millisecondsTimeout, cancellationToken);
+ }
+ }
+ }
+
+ /// <summary>Creates a new task and stores it into the async waiters list.</summary>
+ /// <returns>The created task.</returns>
+ private TaskNode CreateAndAddAsyncWaiter()
+ {
+ Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+ // Create the task
+ var task = new TaskNode();
+
+ // Add it to the linked list
+ if (m_asyncHead == null)
+ {
+ Contract.Assert(m_asyncTail == null, "If head is null, so too should be tail");
+ m_asyncHead = task;
+ m_asyncTail = task;
+ }
+ else
+ {
+ Contract.Assert(m_asyncTail != null, "If head is not null, neither should be tail");
+ m_asyncTail.Next = task;
+ task.Prev = m_asyncTail;
+ m_asyncTail = task;
+ }
+
+ // Hand it back
+ return task;
+ }
+
+ /// <summary>Removes the waiter task from the linked list.</summary>
+ /// <param name="task">The task to remove.</param>
+ /// <returns>true if the waiter was in the list; otherwise, false.</returns>
+ private bool RemoveAsyncWaiter(TaskNode task)
+ {
+ Contract.Requires(task != null, "Expected non-null task");
+ Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+ // Is the task in the list? To be in the list, either it's the head or it has a predecessor that's in the list.
+ bool wasInList = m_asyncHead == task || task.Prev != null;
+
+ // Remove it from the linked list
+ if (task.Next != null) task.Next.Prev = task.Prev;
+ if (task.Prev != null) task.Prev.Next = task.Next;
+ if (m_asyncHead == task) m_asyncHead = task.Next;
+ if (m_asyncTail == task) m_asyncTail = task.Prev;
+ Contract.Assert((m_asyncHead == null) == (m_asyncTail == null), "Head is null iff tail is null");
+
+ // Make sure not to leak
+ task.Next = task.Prev = null;
+
+ // Return whether the task was in the list
+ return wasInList;
+ }
+
+ /// <summary>Performs the asynchronous wait.</summary>
+ /// <param name="millisecondsTimeout">The timeout.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The task to return to the caller.</returns>
+ private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ Contract.Assert(asyncWaiter != null, "Waiter should have been constructed");
+ Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+ // Wait until either the task is completed, timeout occurs, or cancellation is requested.
+ // We need to ensure that the Task.Delay task is appropriately cleaned up if the await
+ // completes due to the asyncWaiter completing, so we use our own token that we can explicitly
+ // cancel, and we chain the caller's supplied token into it.
+ using (var cts = cancellationToken.CanBeCanceled ?
+ CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default(CancellationToken)) :
+ new CancellationTokenSource())
+ {
+ var waitCompleted = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
+ if (asyncWaiter == await waitCompleted.ConfigureAwait(false))
+ {
+ cts.Cancel(); // ensure that the Task.Delay task is cleaned up
+ return true; // successfully acquired
+ }
+ }
+
+ // If we get here, the wait has timed out or been canceled.
+
+ // If the await completed synchronously, we still hold the lock. If it didn't,
+ // we no longer hold the lock. As such, acquire it.
+ lock (m_lockObj)
+ {
+ // Remove the task from the list. If we're successful in doing so,
+ // we know that no one else has tried to complete this waiter yet,
+ // so we can safely cancel or timeout.
+ if (RemoveAsyncWaiter(asyncWaiter))
+ {
+ cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
+ return false; // timeout occurred
+ }
+ }
+
+ // The waiter had already been removed, which means it's already completed or is about to
+ // complete, so let it, and don't return until it does.
+ return await asyncWaiter.ConfigureAwait(false);
+ }
+
+ /// <summary>
+ /// Exits the <see cref="SemaphoreSlim"/> once.
+ /// </summary>
+ /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public int Release()
+ {
+ return Release(1);
+ }
+
+ /// <summary>
+ /// Exits the <see cref="SemaphoreSlim"/> a specified number of times.
+ /// </summary>
+ /// <param name="releaseCount">The number of times to exit the semaphore.</param>
+ /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="releaseCount"/> is less
+ /// than 1.</exception>
+ /// <exception cref="T:System.Threading.SemaphoreFullException">The <see cref="SemaphoreSlim"/> has
+ /// already reached its maximum size.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+ /// disposed.</exception>
+ public int Release(int releaseCount)
+ {
+ CheckDispose();
+
+ // Validate input
+ if (releaseCount < 1)
+ {
+ throw new ArgumentOutOfRangeException(
+ "releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong"));
+ }
+ int returnCount;
+
+ lock (m_lockObj)
+ {
+ // Read the m_currentCount into a local variable to avoid unnecessary volatile accesses inside the lock.
+ int currentCount = m_currentCount;
+ returnCount = currentCount;
+
+ // If the release count would result exceeding the maximum count, throw SemaphoreFullException.
+ if (m_maxCount - currentCount < releaseCount)
+ {
+ throw new SemaphoreFullException();
+ }
+
+ // Increment the count by the actual release count
+ currentCount += releaseCount;
+
+ // Signal to any synchronous waiters
+ int waitCount = m_waitCount;
+ if (currentCount == 1 || waitCount == 1)
+ {
+ Monitor.Pulse(m_lockObj);
+ }
+ else if (waitCount > 1)
+ {
+ Monitor.PulseAll(m_lockObj);
+ }
+
+ // Now signal to any asynchronous waiters, if there are any. While we've already
+ // signaled the synchronous waiters, we still hold the lock, and thus
+ // they won't have had an opportunity to acquire this yet. So, when releasing
+ // asynchronous waiters, we assume that all synchronous waiters will eventually
+ // acquire the semaphore. That could be a faulty assumption if those synchronous
+ // waits are canceled, but the wait code path will handle that.
+ if (m_asyncHead != null)
+ {
+ Contract.Assert(m_asyncTail != null, "tail should not be null if head isn't null");
+ int maxAsyncToRelease = currentCount - waitCount;
+ while (maxAsyncToRelease > 0 && m_asyncHead != null)
+ {
+ --currentCount;
+ --maxAsyncToRelease;
+
+ // Get the next async waiter to release and queue it to be completed
+ var waiterTask = m_asyncHead;
+ RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
+ QueueWaiterTask(waiterTask);
+ }
+ }
+ m_currentCount = currentCount;
+
+ // Exposing wait handle if it is not null
+ if (m_waitHandle != null && returnCount == 0 && currentCount > 0)
+ {
+ m_waitHandle.Set();
+ }
+ }
+
+ // And return the count
+ return returnCount;
+ }
+
+ /// <summary>
+ /// Queues a waiter task to the ThreadPool. We use this small helper method so that
+ /// the larger Release(count) method does not need to be SecuritySafeCritical.
+ /// </summary>
+ [SecuritySafeCritical] // for ThreadPool.UnsafeQueueCustomWorkItem
+ private static void QueueWaiterTask(TaskNode waiterTask)
+ {
+ ThreadPool.UnsafeQueueCustomWorkItem(waiterTask, forceGlobal: false);
+ }
+
+ /// <summary>
+ /// Releases all resources used by the current instance of <see
+ /// cref="SemaphoreSlim"/>.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose()"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, releases the unmanaged resources used by the
+ /// <see cref="T:System.Threading.ManualResetEventSlim"/>, and optionally releases the managed resources.
+ /// </summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources;
+ /// false to release only unmanaged resources.</param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose(Boolean)"/> is not
+ /// thread-safe and may not be used concurrently with other members of this instance.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (m_waitHandle != null)
+ {
+ m_waitHandle.Close();
+ m_waitHandle = null;
+ }
+ m_lockObj = null;
+ m_asyncHead = null;
+ m_asyncTail = null;
+ }
+ }
+
+
+
+ /// <summary>
+ /// Private helper method to wake up waiters when a cancellationToken gets canceled.
+ /// </summary>
+ private static Action<object> s_cancellationTokenCanceledEventHandler = new Action<object>(CancellationTokenCanceledEventHandler);
+ private static void CancellationTokenCanceledEventHandler(object obj)
+ {
+ SemaphoreSlim semaphore = obj as SemaphoreSlim;
+ Contract.Assert(semaphore != null, "Expected a SemaphoreSlim");
+ lock (semaphore.m_lockObj)
+ {
+ Monitor.PulseAll(semaphore.m_lockObj); //wake up all waiters.
+ }
+ }
+
+ /// <summary>
+ /// Checks the dispose status by checking the lock object, if it is null means that object
+ /// has been disposed and throw ObjectDisposedException
+ /// </summary>
+ private void CheckDispose()
+ {
+ if (m_lockObj == null)
+ {
+ throw new ObjectDisposedException(null, GetResourceString("SemaphoreSlim_Disposed"));
+ }
+ }
+
+ /// <summary>
+ /// local helper function to retrieve the exception string message from the resource file
+ /// </summary>
+ /// <param name="str">The key string</param>
+ private static string GetResourceString(string str)
+ {
+ return Environment.GetResourceString(str);
+ }
+ #endregion
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/SendOrPostCallback.cs b/src/mscorlib/src/System/Threading/SendOrPostCallback.cs
new file mode 100644
index 0000000000..b81d2bff64
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SendOrPostCallback.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+/*============================================================
+**
+**
+**
+** Purpose: Represents a method to be called when a message is to be dispatched to a synchronization context.
+**
+**
+===========================================================*/
+
+namespace System.Threading
+{
+ public delegate void SendOrPostCallback(Object state);
+}
diff --git a/src/mscorlib/src/System/Threading/SpinLock.cs b/src/mscorlib/src/System/Threading/SpinLock.cs
new file mode 100644
index 0000000000..dea87435a7
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SpinLock.cs
@@ -0,0 +1,750 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// A spin lock is a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop ("spins")
+// repeatedly checking until the lock becomes available. As the thread remains active performing a non-useful task,
+// the use of such a lock is a kind of busy waiting and consumes CPU resources without performing real work.
+//
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+using System;
+using System.Diagnostics;
+using System.Security.Permissions;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Runtime.ConstrainedExecution;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading
+{
+
+ /// <summary>
+ /// Provides a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop
+ /// repeatedly checking until the lock becomes available.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Spin locks can be used for leaf-level locks where the object allocation implied by using a <see
+ /// cref="System.Threading.Monitor"/>, in size or due to garbage collection pressure, is overly
+ /// expensive. Avoiding blocking is another reason that a spin lock can be useful, however if you expect
+ /// any significant amount of blocking, you are probably best not using spin locks due to excessive
+ /// spinning. Spinning can be beneficial when locks are fine grained and large in number (for example, a
+ /// lock per node in a linked list) as well as when lock hold times are always extremely short. In
+ /// general, while holding a spin lock, one should avoid blocking, calling anything that itself may
+ /// block, holding more than one spin lock at once, making dynamically dispatched calls (interface and
+ /// virtuals), making statically dispatched calls into any code one doesn't own, or allocating memory.
+ /// </para>
+ /// <para>
+ /// <see cref="SpinLock"/> should only be used when it's been determined that doing so will improve an
+ /// application's performance. It's also important to note that <see cref="SpinLock"/> is a value type,
+ /// for performance reasons. As such, one must be very careful not to accidentally copy a SpinLock
+ /// instance, as the two instances (the original and the copy) would then be completely independent of
+ /// one another, which would likely lead to erroneous behavior of the application. If a SpinLock instance
+ /// must be passed around, it should be passed by reference rather than by value.
+ /// </para>
+ /// <para>
+ /// Do not store <see cref="SpinLock"/> instances in readonly fields.
+ /// </para>
+ /// <para>
+ /// All members of <see cref="SpinLock"/> are thread-safe and may be used from multiple threads
+ /// concurrently.
+ /// </para>
+ /// </remarks>
+ [ComVisible(false)]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerTypeProxy(typeof(SystemThreading_SpinLockDebugView))]
+ [DebuggerDisplay("IsHeld = {IsHeld}")]
+ public struct SpinLock
+ {
+ // The current ownership state is a single signed int. There are two modes:
+ //
+ // 1) Ownership tracking enabled: the high bit is 0, and the remaining bits
+ // store the managed thread ID of the current owner. When the 31 low bits
+ // are 0, the lock is available.
+ // 2) Performance mode: when the high bit is 1, lock availability is indicated by the low bit.
+ // When the low bit is 1 -- the lock is held; 0 -- the lock is available.
+ //
+ // There are several masks and constants below for convenience.
+
+ private volatile int m_owner;
+
+ // The multiplier factor for the each spinning iteration
+ // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results
+ private const int SPINNING_FACTOR = 100;
+
+ // After how many yields, call Sleep(1)
+ private const int SLEEP_ONE_FREQUENCY = 40;
+
+ // After how many yields, call Sleep(0)
+ private const int SLEEP_ZERO_FREQUENCY = 10;
+
+ // After how many yields, check the timeout
+ private const int TIMEOUT_CHECK_FREQUENCY = 10;
+
+ // Thr thread tracking disabled mask
+ private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000); //1000 0000 0000 0000 0000 0000 0000 0000
+
+ //the lock is held by some thread, but we don't know which
+ private const int LOCK_ANONYMOUS_OWNED = 0x1; //0000 0000 0000 0000 0000 0000 0000 0001
+
+ // Waiters mask if the thread tracking is disabled
+ private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1); //0111 1111 1111 1111 1111 1111 1111 1110
+
+ // The Thread tacking is disabled and the lock bit is set, used in Enter fast path to make sure the id is disabled and lock is available
+ private const int ID_DISABLED_AND_ANONYMOUS_OWNED = unchecked((int)0x80000001); //1000 0000 0000 0000 0000 0000 0000 0001
+
+ // If the thread is unowned if:
+ // m_owner zero and the threa tracking is enabled
+ // m_owner & LOCK_ANONYMOUS_OWNED = zero and the thread tracking is disabled
+ private const int LOCK_UNOWNED = 0;
+
+ // The maximum number of waiters (only used if the thread tracking is disabled)
+ // The actual maximum waiters count is this number divided by two because each waiter increments the waiters count by 2
+ // The waiters count is calculated by m_owner & WAITERS_MASK 01111....110
+ private static int MAXIMUM_WAITERS = WAITERS_MASK;
+
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/>
+ /// structure with the option to track thread IDs to improve debugging.
+ /// </summary>
+ /// <remarks>
+ /// The default constructor for <see cref="SpinLock"/> tracks thread ownership.
+ /// </remarks>
+ /// <param name="enableThreadOwnerTracking">Whether to capture and use thread IDs for debugging
+ /// purposes.</param>
+ public SpinLock(bool enableThreadOwnerTracking)
+ {
+ m_owner = LOCK_UNOWNED;
+ if (!enableThreadOwnerTracking)
+ {
+ m_owner |= LOCK_ID_DISABLE_MASK;
+ Contract.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now");
+ }
+ }
+
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/>
+ /// structure with the option to track thread IDs to improve debugging.
+ /// </summary>
+ /// <remarks>
+ /// The default constructor for <see cref="SpinLock"/> tracks thread ownership.
+ /// </remarks>
+ /// <summary>
+ /// Acquires the lock in a reliable manner, such that even if an exception occurs within the method
+ /// call, <paramref name="lockTaken"/> can be examined reliably to determine whether the lock was
+ /// acquired.
+ /// </summary>
+ /// <remarks>
+ /// <see cref="SpinLock"/> is a non-reentrant lock, meaning that if a thread holds the lock, it is
+ /// not allowed to enter the lock again. If thread ownership tracking is enabled (whether it's
+ /// enabled is available through <see cref="IsThreadOwnerTrackingEnabled"/>), an exception will be
+ /// thrown when a thread tries to re-enter a lock it already holds. However, if thread ownership
+ /// tracking is disabled, attempting to enter a lock already held will result in deadlock.
+ /// </remarks>
+ /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
+ /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
+ /// <exception cref="T:System.Threading.LockRecursionException">
+ /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling Enter.
+ /// </exception>
+ public void Enter(ref bool lockTaken)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ //Try to keep the code and branching in this method as small as possible in order to inline the method
+ int observedOwner = m_owner;
+ if (lockTaken || //invalid parameter
+ (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired
+ Interlocked.CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) //acquiring the lock failed
+ ContinueTryEnter(Timeout.Infinite, ref lockTaken); // Then try the slow path if any of the above conditions is met
+
+ }
+
+ /// <summary>
+ /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
+ /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
+ /// lock was acquired.
+ /// </summary>
+ /// <remarks>
+ /// Unlike <see cref="Enter"/>, TryEnter will not block waiting for the lock to be available. If the
+ /// lock is not available when TryEnter is called, it will return immediately without any further
+ /// spinning.
+ /// </remarks>
+ /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
+ /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
+ /// <exception cref="T:System.Threading.LockRecursionException">
+ /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
+ /// </exception>
+ public void TryEnter(ref bool lockTaken)
+ {
+ TryEnter(0, ref lockTaken);
+ }
+
+ /// <summary>
+ /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
+ /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
+ /// lock was acquired.
+ /// </summary>
+ /// <remarks>
+ /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be
+ /// available. It will block until either the lock is available or until the <paramref
+ /// name="timeout"/>
+ /// has expired.
+ /// </remarks>
+ /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+ /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
+ /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
+ /// <exception cref="T:System.Threading.LockRecursionException">
+ /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+ /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+ /// than <see cref="System.Int32.MaxValue"/> milliseconds.
+ /// </exception>
+ public void TryEnter(TimeSpan timeout, ref bool lockTaken)
+ {
+ // Validate the timeout
+ Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+ {
+ throw new System.ArgumentOutOfRangeException(
+ "timeout", timeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange"));
+ }
+
+ // Call reliable enter with the int-based timeout milliseconds
+ TryEnter((int)timeout.TotalMilliseconds, ref lockTaken);
+ }
+
+ /// <summary>
+ /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
+ /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
+ /// lock was acquired.
+ /// </summary>
+ /// <remarks>
+ /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be
+ /// available. It will block until either the lock is available or until the <paramref
+ /// name="millisecondsTimeout"/> has expired.
+ /// </remarks>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param>
+ /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
+ /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
+ /// <exception cref="T:System.Threading.LockRecursionException">
+ /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is
+ /// a negative number other than -1, which represents an infinite time-out.</exception>
+ public void TryEnter(int millisecondsTimeout, ref bool lockTaken)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+
+ int observedOwner = m_owner;
+ if (millisecondsTimeout < -1 || //invalid parameter
+ lockTaken || //invalid parameter
+ (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired
+ Interlocked.CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) // acquiring the lock failed
+ ContinueTryEnter(millisecondsTimeout, ref lockTaken); // The call the slow pth
+ }
+
+ /// <summary>
+ /// Try acquire the lock with long path, this is usually called after the first path in Enter and
+ /// TryEnter failed The reason for short path is to make it inline in the run time which improves the
+ /// performance. This method assumed that the parameter are validated in Enter ir TryENter method
+ /// </summary>
+ /// <param name="millisecondsTimeout">The timeout milliseconds</param>
+ /// <param name="lockTaken">The lockTaken param</param>
+ private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)
+ {
+ //Leave the critical region which is entered by the fast path
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ // The fast path doesn't throw any exception, so we have to validate the parameters here
+ if (lockTaken)
+ {
+ lockTaken = false;
+ throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException"));
+ }
+
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException(
+ "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange"));
+ }
+
+
+ uint startTime = 0;
+ if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0)
+ {
+ startTime = TimeoutHelper.GetTime();
+ }
+
+#if !FEATURE_CORECLR
+ if (CdsSyncEtwBCLProvider.Log.IsEnabled())
+ {
+ CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner);
+ }
+#endif
+
+ if (IsThreadOwnerTrackingEnabled)
+ {
+ // Slow path for enabled thread tracking mode
+ ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken);
+ return;
+ }
+
+ // then thread tracking is disabled
+ // In this case there are three ways to acquire the lock
+ // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2
+ // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn
+ // the late the thread arrives the more it spins and less frequent it check the lock avilability
+ // Also the spins count is increases each iteration
+ // If the spins iterations finished and failed to acquire the lock, go to step 3
+ // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1)
+ // If the timeout is expired in after step 1, we need to decrement the waiters count before returning
+
+ int observedOwner;
+ int turn = int.MaxValue;
+ //***Step 1, take the lock or update the waiters
+
+ // try to acquire the lock directly if possible or update the waiters count
+ observedOwner = m_owner;
+ if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+
+ if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
+ {
+ return;
+ }
+
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ }
+ else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow
+ {
+ if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS)
+ turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1 ;
+ }
+
+
+
+ // Check the timeout.
+ if (millisecondsTimeout == 0 ||
+ (millisecondsTimeout != Timeout.Infinite &&
+ TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0))
+ {
+ DecrementWaiters();
+ return;
+ }
+
+ //***Step 2. Spinning
+ //lock acquired failed and waiters updated
+ int processorCount = PlatformHelper.ProcessorCount;
+ if (turn < processorCount)
+ {
+ int processFactor = 1;
+ for (int i = 1; i <= turn * SPINNING_FACTOR; i++)
+ {
+ Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor);
+ if (processFactor < processorCount)
+ processFactor++;
+ observedOwner = m_owner;
+ if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+
+ int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
+ observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
+ : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
+ Contract.Assert((newOwner & WAITERS_MASK) >= 0);
+
+ if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
+ {
+ return;
+ }
+
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ }
+ }
+ }
+
+ // Check the timeout.
+ if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
+ {
+ DecrementWaiters();
+ return;
+ }
+
+ //*** Step 3, Yielding
+ //Sleep(1) every 50 yields
+ int yieldsoFar = 0;
+ while (true)
+ {
+ observedOwner = m_owner;
+ if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
+ observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
+ : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
+ Contract.Assert((newOwner & WAITERS_MASK) >= 0);
+
+ if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
+ {
+ return;
+ }
+
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ }
+
+ if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0)
+ {
+ Thread.Sleep(1);
+ }
+ else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0)
+ {
+ Thread.Sleep(0);
+ }
+ else
+ {
+ Thread.Yield();
+ }
+
+ if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0)
+ {
+ //Check the timeout.
+ if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
+ {
+ DecrementWaiters();
+ return;
+ }
+ }
+
+ yieldsoFar++;
+ }
+ }
+
+ /// <summary>
+ /// decrements the waiters, in case of the timeout is expired
+ /// </summary>
+ private void DecrementWaiters()
+ {
+ SpinWait spinner = new SpinWait();
+ while (true)
+ {
+ int observedOwner = m_owner;
+ if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false)
+ if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner)
+ {
+ Contract.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped
+ break;
+ }
+ spinner.SpinOnce();
+ }
+
+ }
+
+ /// <summary>
+ /// ContinueTryEnter for the thread tracking mode enabled
+ /// </summary>
+ private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, uint startTime, ref bool lockTaken)
+ {
+ Contract.Assert(IsThreadOwnerTrackingEnabled);
+
+ int lockUnowned = 0;
+ // We are using thread IDs to mark ownership. Snap the thread ID and check for recursion.
+ // We also must or the ID enablement bit, to ensure we propagate when we CAS it in.
+ int m_newOwner = Thread.CurrentThread.ManagedThreadId;
+ if (m_owner == m_newOwner)
+ {
+ // We don't allow lock recursion.
+ throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException"));
+ }
+
+
+ SpinWait spinner = new SpinWait();
+
+ // Loop until the lock has been successfully acquired or, if specified, the timeout expires.
+ do
+ {
+
+ // We failed to get the lock, either from the fast route or the last iteration
+ // and the timeout hasn't expired; spin once and try again.
+ spinner.SpinOnce();
+
+ // Test before trying to CAS, to avoid acquiring the line exclusively unnecessarily.
+
+ if (m_owner == lockUnowned)
+ {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned)
+ {
+ return;
+ }
+#if !FEATURE_CORECLR
+ // The thread failed to get the lock, so we don't need to remain in a critical region.
+ Thread.EndCriticalRegion();
+#endif
+ }
+ // Check the timeout. We only RDTSC if the next spin will yield, to amortize the cost.
+ if (millisecondsTimeout == 0 ||
+ (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield &&
+ TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0))
+ {
+ return;
+ }
+ } while (true);
+ }
+
+ /// <summary>
+ /// Releases the lock.
+ /// </summary>
+ /// <remarks>
+ /// The default overload of <see cref="Exit()"/> provides the same behavior as if calling <see
+ /// cref="Exit(Boolean)"/> using true as the argument, but Exit() could be slightly faster than Exit(true).
+ /// </remarks>
+ /// <exception cref="SynchronizationLockException">
+ /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock.
+ /// </exception>
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public void Exit()
+ {
+ //This is the fast path for the thread tracking is disabled, otherwise go to the slow path
+ if ((m_owner & LOCK_ID_DISABLE_MASK) == 0)
+ ExitSlowPath(true);
+ else
+ Interlocked.Decrement(ref m_owner);
+
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+
+ }
+
+ /// <summary>
+ /// Releases the lock.
+ /// </summary>
+ /// <param name="useMemoryBarrier">
+ /// A Boolean value that indicates whether a memory fence should be issued in order to immediately
+ /// publish the exit operation to other threads.
+ /// </param>
+ /// <remarks>
+ /// Calling <see cref="Exit(Boolean)"/> with the <paramref name="useMemoryBarrier"/> argument set to
+ /// true will improve the fairness of the lock at the expense of some performance. The default <see
+ /// cref="Enter"/>
+ /// overload behaves as if specifying true for <paramref name="useMemoryBarrier"/>.
+ /// </remarks>
+ /// <exception cref="SynchronizationLockException">
+ /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock.
+ /// </exception>
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public void Exit(bool useMemoryBarrier)
+ {
+ // This is the fast path for the thread tracking is diabled and not to use memory barrier, otherwise go to the slow path
+ // The reason not to add else statement if the usememorybarrier is that it will add more barnching in the code and will prevent
+ // method inlining, so this is optimized for useMemoryBarrier=false and Exit() overload optimized for useMemoryBarrier=true
+ if ((m_owner & LOCK_ID_DISABLE_MASK) != 0 && !useMemoryBarrier)
+ {
+ int tmpOwner = m_owner;
+ m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED);
+ }
+ else
+ ExitSlowPath(useMemoryBarrier);
+
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ }
+
+ /// <summary>
+ /// The slow path for exit method if the fast path failed
+ /// </summary>
+ /// <param name="useMemoryBarrier">
+ /// A Boolean value that indicates whether a memory fence should be issued in order to immediately
+ /// publish the exit operation to other threads
+ /// </param>
+ private void ExitSlowPath(bool useMemoryBarrier)
+ {
+ bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0;
+ if (threadTrackingEnabled && !IsHeldByCurrentThread)
+ {
+ throw new System.Threading.SynchronizationLockException(
+ Environment.GetResourceString("SpinLock_Exit_SynchronizationLockException"));
+ }
+
+ if (useMemoryBarrier)
+ {
+ if (threadTrackingEnabled)
+ Interlocked.Exchange(ref m_owner, LOCK_UNOWNED);
+ else
+ Interlocked.Decrement(ref m_owner);
+
+ }
+ else
+ {
+ if (threadTrackingEnabled)
+ m_owner = LOCK_UNOWNED;
+ else
+ {
+ int tmpOwner = m_owner;
+ m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED);
+ }
+
+ }
+
+ }
+
+ /// <summary>
+ /// Gets whether the lock is currently held by any thread.
+ /// </summary>
+ public bool IsHeld
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get
+ {
+ if (IsThreadOwnerTrackingEnabled)
+ return m_owner != LOCK_UNOWNED;
+
+ return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the lock is currently held by any thread.
+ /// </summary>
+ /// <summary>
+ /// Gets whether the lock is held by the current thread.
+ /// </summary>
+ /// <remarks>
+ /// If the lock was initialized to track owner threads, this will return whether the lock is acquired
+ /// by the current thread. It is invalid to use this property when the lock was initialized to not
+ /// track thread ownership.
+ /// </remarks>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// Thread ownership tracking is disabled.
+ /// </exception>
+ public bool IsHeldByCurrentThread
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get
+ {
+ if (!IsThreadOwnerTrackingEnabled)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("SpinLock_IsHeldByCurrentThread"));
+ }
+ return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Thread.CurrentThread.ManagedThreadId);
+ }
+ }
+
+ /// <summary>Gets whether thread ownership tracking is enabled for this instance.</summary>
+ public bool IsThreadOwnerTrackingEnabled
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; }
+ }
+
+ #region Debugger proxy class
+ /// <summary>
+ /// Internal class used by debug type proxy attribute to display the owner thread ID
+ /// </summary>
+ internal class SystemThreading_SpinLockDebugView
+ {
+ // SpinLock object
+ private SpinLock m_spinLock;
+
+ /// <summary>
+ /// SystemThreading_SpinLockDebugView constructor
+ /// </summary>
+ /// <param name="spinLock">The SpinLock to be proxied.</param>
+ public SystemThreading_SpinLockDebugView(SpinLock spinLock)
+ {
+ // Note that this makes a copy of the SpinLock (struct). It doesn't hold a reference to it.
+ m_spinLock = spinLock;
+ }
+
+ /// <summary>
+ /// Checks if the lock is held by the current thread or not
+ /// </summary>
+ public bool? IsHeldByCurrentThread
+ {
+ get
+ {
+ try
+ {
+ return m_spinLock.IsHeldByCurrentThread;
+ }
+ catch (InvalidOperationException)
+ {
+ return null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the current owner thread, zero if it is released
+ /// </summary>
+ public int? OwnerThreadID
+ {
+ get
+ {
+ if (m_spinLock.IsThreadOwnerTrackingEnabled)
+ {
+ return m_spinLock.m_owner;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Gets whether the lock is currently held by any thread or not.
+ /// </summary>
+ public bool IsHeld
+ {
+ get { return m_spinLock.IsHeld; }
+ }
+ }
+ #endregion
+
+ }
+}
+#pragma warning restore 0420
diff --git a/src/mscorlib/src/System/Threading/SpinWait.cs b/src/mscorlib/src/System/Threading/SpinWait.cs
new file mode 100644
index 0000000000..c2cd0b6203
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SpinWait.cs
@@ -0,0 +1,369 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Central spin logic used across the entire code-base.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Security.Permissions;
+using System.Threading;
+using System.Diagnostics.Contracts;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Threading
+{
+ // SpinWait is just a little value type that encapsulates some common spinning
+ // logic. It ensures we always yield on single-proc machines (instead of using busy
+ // waits), and that we work well on HT. It encapsulates a good mixture of spinning
+ // and real yielding. It's a value type so that various areas of the engine can use
+ // one by allocating it on the stack w/out unnecessary GC allocation overhead, e.g.:
+ //
+ // void f() {
+ // SpinWait wait = new SpinWait();
+ // while (!p) { wait.SpinOnce(); }
+ // ...
+ // }
+ //
+ // Internally it just maintains a counter that is used to decide when to yield, etc.
+ //
+ // A common usage is to spin before blocking. In those cases, the NextSpinWillYield
+ // property allows a user to decide to fall back to waiting once it returns true:
+ //
+ // void f() {
+ // SpinWait wait = new SpinWait();
+ // while (!p) {
+ // if (wait.NextSpinWillYield) { /* block! */ }
+ // else { wait.SpinOnce(); }
+ // }
+ // ...
+ // }
+
+ /// <summary>
+ /// Provides support for spin-based waiting.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// <see cref="SpinWait"/> encapsulates common spinning logic. On single-processor machines, yields are
+ /// always used instead of busy waits, and on computers with Intel™ processors employing Hyper-Threading™
+ /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of
+ /// spinning and true yielding.
+ /// </para>
+ /// <para>
+ /// <see cref="SpinWait"/> is a value type, which means that low-level code can utilize SpinWait without
+ /// fear of unnecessary allocation overheads. SpinWait is not generally useful for ordinary applications.
+ /// In most cases, you should use the synchronization classes provided by the .NET Framework, such as
+ /// <see cref="System.Threading.Monitor"/>. For most purposes where spin waiting is required, however,
+ /// the <see cref="SpinWait"/> type should be preferred over the <see
+ /// cref="System.Threading.Thread.SpinWait"/> method.
+ /// </para>
+ /// <para>
+ /// While SpinWait is designed to be used in concurrent applications, it is not designed to be
+ /// used from multiple threads concurrently. SpinWait's members are not thread-safe. If multiple
+ /// threads must spin, each should use its own instance of SpinWait.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public struct SpinWait
+ {
+
+ // These constants determine the frequency of yields versus spinning. The
+ // numbers may seem fairly arbitrary, but were derived with at least some
+ // thought in the design document. I fully expect they will need to change
+ // over time as we gain more experience with performance.
+ internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield.
+ internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)?
+ internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)?
+
+ // The number of times we've spun already.
+ private int m_count;
+
+ /// <summary>
+ /// Gets the number of times <see cref="SpinOnce"/> has been called on this instance.
+ /// </summary>
+ public int Count
+ {
+ get { return m_count; }
+ }
+
+ /// <summary>
+ /// Gets whether the next call to <see cref="SpinOnce"/> will yield the processor, triggering a
+ /// forced context switch.
+ /// </summary>
+ /// <value>Whether the next call to <see cref="SpinOnce"/> will yield the processor, triggering a
+ /// forced context switch.</value>
+ /// <remarks>
+ /// On a single-CPU machine, <see cref="SpinOnce"/> always yields the processor. On machines with
+ /// multiple CPUs, <see cref="SpinOnce"/> may yield after an unspecified number of calls.
+ /// </remarks>
+ public bool NextSpinWillYield
+ {
+ get { return m_count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; }
+ }
+
+ /// <summary>
+ /// Performs a single spin.
+ /// </summary>
+ /// <remarks>
+ /// This is typically called in a loop, and may change in behavior based on the number of times a
+ /// <see cref="SpinOnce"/> has been called thus far on this instance.
+ /// </remarks>
+ public void SpinOnce()
+ {
+ if (NextSpinWillYield)
+ {
+ //
+ // We must yield.
+ //
+ // We prefer to call Thread.Yield first, triggering a SwitchToThread. This
+ // unfortunately doesn't consider all runnable threads on all OS SKUs. In
+ // some cases, it may only consult the runnable threads whose ideal processor
+ // is the one currently executing code. Thus we occasionally issue a call to
+ // Sleep(0), which considers all runnable threads at equal priority. Even this
+ // is insufficient since we may be spin waiting for lower priority threads to
+ // execute; we therefore must call Sleep(1) once in a while too, which considers
+ // all runnable threads, regardless of ideal processor and priority, but may
+ // remove the thread from the scheduler's queue for 10+ms, if the system is
+ // configured to use the (default) coarse-grained system timer.
+ //
+
+#if !FEATURE_CORECLR
+ CdsSyncEtwBCLProvider.Log.SpinWait_NextSpinWillYield();
+#endif
+ int yieldsSoFar = (m_count >= YIELD_THRESHOLD ? m_count - YIELD_THRESHOLD : m_count);
+
+ if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1))
+ {
+ Thread.Sleep(1);
+ }
+ else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1))
+ {
+ Thread.Sleep(0);
+ }
+ else
+ {
+ Thread.Yield();
+ }
+ }
+ else
+ {
+ //
+ // Otherwise, we will spin.
+ //
+ // We do this using the CLR's SpinWait API, which is just a busy loop that
+ // issues YIELD/PAUSE instructions to ensure multi-threaded CPUs can react
+ // intelligently to avoid starving. (These are NOOPs on other CPUs.) We
+ // choose a number for the loop iteration count such that each successive
+ // call spins for longer, to reduce cache contention. We cap the total
+ // number of spins we are willing to tolerate to reduce delay to the caller,
+ // since we expect most callers will eventually block anyway.
+ //
+ Thread.SpinWait(4 << m_count);
+ }
+
+ // Finally, increment our spin counter.
+ m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1);
+ }
+
+ /// <summary>
+ /// Resets the spin counter.
+ /// </summary>
+ /// <remarks>
+ /// This makes <see cref="SpinOnce"/> and <see cref="NextSpinWillYield"/> behave as though no calls
+ /// to <see cref="SpinOnce"/> had been issued on this instance. If a <see cref="SpinWait"/> instance
+ /// is reused many times, it may be useful to reset it to avoid yielding too soon.
+ /// </remarks>
+ public void Reset()
+ {
+ m_count = 0;
+ }
+
+ #region Static Methods
+ /// <summary>
+ /// Spins until the specified condition is satisfied.
+ /// </summary>
+ /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
+ /// <exception cref="ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
+ public static void SpinUntil(Func<bool> condition)
+ {
+#if DEBUG
+ bool result =
+#endif
+ SpinUntil(condition, Timeout.Infinite);
+#if DEBUG
+ Contract.Assert(result);
+#endif
+ }
+
+ /// <summary>
+ /// Spins until the specified condition is satisfied or until the specified timeout is expired.
+ /// </summary>
+ /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
+ /// <param name="timeout">
+ /// A <see cref="TimeSpan"/> that represents the number of milliseconds to wait,
+ /// or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+ /// <returns>True if the condition is satisfied within the timeout; otherwise, false</returns>
+ /// <exception cref="ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative number
+ /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than
+ /// <see cref="System.Int32.MaxValue"/>.</exception>
+ public static bool SpinUntil(Func<bool> condition, TimeSpan timeout)
+ {
+ // Validate the timeout
+ Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ throw new System.ArgumentOutOfRangeException(
+ "timeout", timeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong"));
+ }
+
+ // Call wait with the timeout milliseconds
+ return SpinUntil(condition, (int)timeout.TotalMilliseconds);
+ }
+
+ /// <summary>
+ /// Spins until the specified condition is satisfied or until the specified timeout is expired.
+ /// </summary>
+ /// <param name="condition">A delegate to be executed over and over until it returns true.</param>
+ /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+ /// cref="System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param>
+ /// <returns>True if the condition is satisfied within the timeout; otherwise, false</returns>
+ /// <exception cref="ArgumentNullException">The <paramref name="condition"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+ /// negative number other than -1, which represents an infinite time-out.</exception>
+ public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout)
+ {
+ if (millisecondsTimeout < Timeout.Infinite)
+ {
+ throw new ArgumentOutOfRangeException(
+ "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong"));
+ }
+ if (condition == null)
+ {
+ throw new ArgumentNullException("condition", Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull"));
+ }
+ uint startTime = 0;
+ if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite)
+ {
+ startTime = TimeoutHelper.GetTime();
+ }
+ SpinWait spinner = new SpinWait();
+ while (!condition())
+ {
+ if (millisecondsTimeout == 0)
+ {
+ return false;
+ }
+
+ spinner.SpinOnce();
+
+ if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield)
+ {
+ if (millisecondsTimeout <= (TimeoutHelper.GetTime() - startTime))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+
+ }
+ #endregion
+
+ }
+
+
+ /// <summary>
+ /// A helper class to get the number of processors, it updates the numbers of processors every sampling interval.
+ /// </summary>
+ internal static class PlatformHelper
+ {
+ private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds.
+ private static volatile int s_processorCount; // The last count seen.
+ private static volatile int s_lastProcessorCountRefreshTicks; // The last time we refreshed.
+
+ /// <summary>
+ /// Gets the number of available processors
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ internal static int ProcessorCount
+ {
+ get
+ {
+ int now = Environment.TickCount;
+ int procCount = s_processorCount;
+ if (procCount == 0 || (now - s_lastProcessorCountRefreshTicks) >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS)
+ {
+ s_processorCount = procCount = Environment.ProcessorCount;
+ s_lastProcessorCountRefreshTicks = now;
+ }
+
+ Contract.Assert(procCount > 0 && procCount <= 64,
+ "Processor count not within the expected range (1 - 64).");
+
+ return procCount;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the current machine has only a single processor.
+ /// </summary>
+ internal static bool IsSingleProcessor
+ {
+ get { return ProcessorCount == 1; }
+ }
+ }
+
+ /// <summary>
+ /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from
+ /// the start time
+ /// </summary>
+ internal static class TimeoutHelper
+ {
+ /// <summary>
+ /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days
+ /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days
+ /// </summary>
+ /// <returns></returns>
+ public static uint GetTime()
+ {
+ return (uint)Environment.TickCount;
+ }
+
+ /// <summary>
+ /// Helper function to measure and update the elapsed time
+ /// </summary>
+ /// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param>
+ /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param>
+ /// <returns>The new wait time in milliseconds, -1 if the time expired</returns>
+ public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout)
+ {
+ // The function must be called in case the time out is not infinite
+ Contract.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite);
+
+ uint elapsedMilliseconds = (GetTime() - startTime);
+
+ // Check the elapsed milliseconds is greater than max int because this property is uint
+ if (elapsedMilliseconds > int.MaxValue)
+ {
+ return 0;
+ }
+
+ // Subtract the elapsed time from the current wait time
+ int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ;
+ if (currentWaitTimeout <= 0)
+ {
+ return 0;
+ }
+
+ return currentWaitTimeout;
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/SynchronizationContext.cs b/src/mscorlib/src/System/Threading/SynchronizationContext.cs
new file mode 100644
index 0000000000..a3f28d1d73
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SynchronizationContext.cs
@@ -0,0 +1,320 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+/*============================================================
+**
+**
+**
+** Purpose: Capture synchronization semantics for asynchronous callbacks
+**
+**
+===========================================================*/
+
+namespace System.Threading
+{
+ using Microsoft.Win32.SafeHandles;
+ using System.Security.Permissions;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+#if FEATURE_CORRUPTING_EXCEPTIONS
+ using System.Runtime.ExceptionServices;
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+ using System.Runtime;
+ using System.Runtime.Versioning;
+ using System.Runtime.ConstrainedExecution;
+ using System.Reflection;
+ using System.Security;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+
+
+#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
+ [Flags]
+ enum SynchronizationContextProperties
+ {
+ None = 0,
+ RequireWaitNotification = 0x1
+ };
+#endif
+
+#if FEATURE_COMINTEROP && FEATURE_APPX
+ //
+ // This is implemented in System.Runtime.WindowsRuntime, allowing us to ask that assembly for a WinRT-specific SyncCtx.
+ // I'd like this to be an interface, or at least an abstract class - but neither seems to play nice with FriendAccessAllowed.
+ //
+ [FriendAccessAllowed]
+ [SecurityCritical]
+ internal class WinRTSynchronizationContextFactoryBase
+ {
+ [SecurityCritical]
+ public virtual SynchronizationContext Create(object coreDispatcher) {return null;}
+ }
+#endif //FEATURE_COMINTEROP
+
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags =SecurityPermissionFlag.ControlPolicy|SecurityPermissionFlag.ControlEvidence)]
+#endif
+ public class SynchronizationContext
+ {
+#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
+ SynchronizationContextProperties _props = SynchronizationContextProperties.None;
+#endif
+
+ public SynchronizationContext()
+ {
+ }
+
+#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
+
+ // helper delegate to statically bind to Wait method
+ private delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
+
+ static Type s_cachedPreparedType1;
+ static Type s_cachedPreparedType2;
+ static Type s_cachedPreparedType3;
+ static Type s_cachedPreparedType4;
+ static Type s_cachedPreparedType5;
+
+ // protected so that only the derived sync context class can enable these flags
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "We never dereference s_cachedPreparedType*, so ordering is unimportant")]
+ protected void SetWaitNotificationRequired()
+ {
+ //
+ // Prepare the method so that it can be called in a reliable fashion when a wait is needed.
+ // This will obviously only make the Wait reliable if the Wait method is itself reliable. The only thing
+ // preparing the method here does is to ensure there is no failure point before the method execution begins.
+ //
+ // Preparing the method in this way is quite expensive, but only needs to be done once per type, per AppDomain.
+ // So we keep track of a few types we've already prepared in this AD. It is uncommon to have more than
+ // a few SynchronizationContext implementations, so we only cache the first five we encounter; this lets
+ // our cache be much faster than a more general cache might be. This is important, because this
+ // is a *very* hot code path for many WPF and WinForms apps.
+ //
+ Type type = this.GetType();
+ if (s_cachedPreparedType1 != type &&
+ s_cachedPreparedType2 != type &&
+ s_cachedPreparedType3 != type &&
+ s_cachedPreparedType4 != type &&
+ s_cachedPreparedType5 != type)
+ {
+ RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
+
+ if (s_cachedPreparedType1 == null) s_cachedPreparedType1 = type;
+ else if (s_cachedPreparedType2 == null) s_cachedPreparedType2 = type;
+ else if (s_cachedPreparedType3 == null) s_cachedPreparedType3 = type;
+ else if (s_cachedPreparedType4 == null) s_cachedPreparedType4 = type;
+ else if (s_cachedPreparedType5 == null) s_cachedPreparedType5 = type;
+ }
+
+ _props |= SynchronizationContextProperties.RequireWaitNotification;
+ }
+
+ public bool IsWaitNotificationRequired()
+ {
+ return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);
+ }
+#endif
+
+
+ public virtual void Send(SendOrPostCallback d, Object state)
+ {
+ d(state);
+ }
+
+ public virtual void Post(SendOrPostCallback d, Object state)
+ {
+ ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
+ }
+
+
+ /// <summary>
+ /// Optional override for subclasses, for responding to notification that operation is starting.
+ /// </summary>
+ public virtual void OperationStarted()
+ {
+ }
+
+ /// <summary>
+ /// Optional override for subclasses, for responding to notification that operation has completed.
+ /// </summary>
+ public virtual void OperationCompleted()
+ {
+ }
+
+#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
+ // Method called when the CLR does a wait operation
+ [System.Security.SecurityCritical] // auto-generated_required
+ [CLSCompliant(false)]
+ [PrePrepareMethod]
+ public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
+ {
+ if (waitHandles == null)
+ {
+ throw new ArgumentNullException("waitHandles");
+ }
+ Contract.EndContractBlock();
+ return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
+ }
+
+ // Static helper to which the above method can delegate to in order to get the default
+ // COM behavior.
+ [System.Security.SecurityCritical] // auto-generated_required
+ [CLSCompliant(false)]
+ [PrePrepareMethod]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ protected static extern int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
+#endif
+
+#if FEATURE_CORECLR
+
+ [System.Security.SecurityCritical]
+ public static void SetSynchronizationContext(SynchronizationContext syncContext)
+ {
+ Thread.CurrentThread.SynchronizationContext = syncContext;
+ }
+
+ [System.Security.SecurityCritical]
+ public static void SetThreadStaticContext(SynchronizationContext syncContext)
+ {
+ Thread.CurrentThread.SynchronizationContext = syncContext;
+ }
+
+ public static SynchronizationContext Current
+ {
+ get
+ {
+ SynchronizationContext context = Thread.CurrentThread.SynchronizationContext;
+
+#if FEATURE_APPX
+ if (context == null && AppDomain.IsAppXModel())
+ context = GetWinRTContext();
+#endif
+
+ return context;
+ }
+ }
+
+ // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)
+ internal static SynchronizationContext CurrentNoFlow
+ {
+ [FriendAccessAllowed]
+ get
+ {
+ return Current; // SC never flows
+ }
+ }
+
+#else //FEATURE_CORECLR
+
+ // set SynchronizationContext on the current thread
+ [System.Security.SecurityCritical] // auto-generated_required
+ public static void SetSynchronizationContext(SynchronizationContext syncContext)
+ {
+ ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext();
+ ec.SynchronizationContext = syncContext;
+ ec.SynchronizationContextNoFlow = syncContext;
+ }
+
+ // Get the current SynchronizationContext on the current thread
+ public static SynchronizationContext Current
+ {
+ get
+ {
+ return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContext ?? GetThreadLocalContext();
+ }
+ }
+
+ // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)
+ internal static SynchronizationContext CurrentNoFlow
+ {
+ [FriendAccessAllowed]
+ get
+ {
+ return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContextNoFlow ?? GetThreadLocalContext();
+ }
+ }
+
+ private static SynchronizationContext GetThreadLocalContext()
+ {
+ SynchronizationContext context = null;
+
+#if FEATURE_APPX
+ if (context == null && AppDomain.IsAppXModel())
+ context = GetWinRTContext();
+#endif
+
+ return context;
+ }
+
+#endif //FEATURE_CORECLR
+
+#if FEATURE_APPX
+ [SecuritySafeCritical]
+ private static SynchronizationContext GetWinRTContext()
+ {
+ Contract.Assert(Environment.IsWinRTSupported);
+ Contract.Assert(AppDomain.IsAppXModel());
+
+ //
+ // We call into the VM to get the dispatcher. This is because:
+ //
+ // a) We cannot call the WinRT APIs directly from mscorlib, because we don't have the fancy projections here.
+ // b) We cannot call into System.Runtime.WindowsRuntime here, because we don't want to load that assembly
+ // into processes that don't need it (for performance reasons).
+ //
+ // So, we check the VM to see if the current thread has a dispatcher; if it does, we pass that along to
+ // System.Runtime.WindowsRuntime to get a corresponding SynchronizationContext.
+ //
+ object dispatcher = GetWinRTDispatcherForCurrentThread();
+ if (dispatcher != null)
+ return GetWinRTSynchronizationContextFactory().Create(dispatcher);
+
+ return null;
+ }
+
+ [SecurityCritical]
+ static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
+
+ [SecurityCritical]
+ private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
+ {
+ //
+ // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the factory via reflection.
+ // It would be better if we could just implement WinRTSynchronizationContextFactory in mscorlib, but we can't, because
+ // we can do very little with WinRT stuff in mscorlib.
+ //
+ WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
+ if (factory == null)
+ {
+ Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
+ s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
+ }
+ return factory;
+ }
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [return: MarshalAs(UnmanagedType.Interface)]
+ private static extern object GetWinRTDispatcherForCurrentThread();
+#endif //FEATURE_APPX
+
+
+ // helper to Clone this SynchronizationContext,
+ public virtual SynchronizationContext CreateCopy()
+ {
+ // the CLR dummy has an empty clone function - no member data
+ return new SynchronizationContext();
+ }
+
+#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
+ [System.Security.SecurityCritical] // auto-generated
+ private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
+ {
+ return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
+ }
+#endif
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/SynchronizationLockException.cs b/src/mscorlib/src/System/Threading/SynchronizationLockException.cs
new file mode 100644
index 0000000000..0610a6063d
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/SynchronizationLockException.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized
+** block of code.
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+
+ using System;
+ using System.Runtime.Serialization;
+ [System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public class SynchronizationLockException : SystemException {
+ public SynchronizationLockException()
+ : base(Environment.GetResourceString("Arg_SynchronizationLockException")) {
+ SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK);
+ }
+
+ public SynchronizationLockException(String message)
+ : base(message) {
+ SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK);
+ }
+
+ public SynchronizationLockException(String message, Exception innerException)
+ : base(message, innerException) {
+ SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK);
+ }
+
+ protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+ }
+
+}
+
+
diff --git a/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs
new file mode 100644
index 0000000000..c29b11a922
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs
@@ -0,0 +1,295 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+//
+
+using System;
+using System.Security;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#if FEATURE_COMINTEROP
+using System.Runtime.InteropServices.WindowsRuntime;
+
+using WFD = Windows.Foundation.Diagnostics;
+#endif
+
+
+namespace System.Threading.Tasks
+{
+
+ [FriendAccessAllowed]
+ internal enum CausalityTraceLevel
+ {
+#if FEATURE_COMINTEROP
+ Required = WFD.CausalityTraceLevel.Required,
+ Important = WFD.CausalityTraceLevel.Important,
+ Verbose = WFD.CausalityTraceLevel.Verbose
+#else
+ Required,
+ Important,
+ Verbose
+#endif
+ }
+
+ [FriendAccessAllowed]
+ internal enum AsyncCausalityStatus
+ {
+#if FEATURE_COMINTEROP
+ Canceled = WFD.AsyncCausalityStatus.Canceled,
+ Completed = WFD.AsyncCausalityStatus.Completed,
+ Error = WFD.AsyncCausalityStatus.Error,
+ Started = WFD.AsyncCausalityStatus.Started
+#else
+ Started,
+ Completed,
+ Canceled,
+ Error
+#endif
+ }
+
+ internal enum CausalityRelation
+ {
+#if FEATURE_COMINTEROP
+ AssignDelegate = WFD.CausalityRelation.AssignDelegate,
+ Join = WFD.CausalityRelation.Join,
+ Choice = WFD.CausalityRelation.Choice,
+ Cancel = WFD.CausalityRelation.Cancel,
+ Error = WFD.CausalityRelation.Error
+#else
+ AssignDelegate,
+ Join,
+ Choice,
+ Cancel,
+ Error
+#endif
+ }
+
+ internal enum CausalitySynchronousWork
+ {
+#if FEATURE_COMINTEROP
+ CompletionNotification = WFD.CausalitySynchronousWork.CompletionNotification,
+ ProgressNotification = WFD.CausalitySynchronousWork.ProgressNotification,
+ Execution = WFD.CausalitySynchronousWork.Execution
+#else
+ CompletionNotification,
+ ProgressNotification,
+ Execution
+#endif
+ }
+
+ [FriendAccessAllowed]
+ internal static class AsyncCausalityTracer
+ {
+ static internal void EnableToETW(bool enabled)
+ {
+#if FEATURE_COMINTEROP
+ if (enabled)
+ f_LoggingOn |= Loggers.ETW;
+ else
+ f_LoggingOn &= ~Loggers.ETW;
+#endif
+ }
+
+ [FriendAccessAllowed]
+ internal static bool LoggingOn
+ {
+ [FriendAccessAllowed]
+ get
+ {
+#if FEATURE_COMINTEROP
+ return f_LoggingOn != 0;
+#else
+ return false;
+#endif
+ }
+ }
+
+#if FEATURE_COMINTEROP
+ //s_PlatformId = {4B0171A6-F3D0-41A0-9B33-02550652B995}
+ private static readonly Guid s_PlatformId = new Guid(0x4B0171A6, 0xF3D0, 0x41A0, 0x9B, 0x33, 0x02, 0x55, 0x06, 0x52, 0xB9, 0x95);
+
+ //Indicates this information comes from the BCL Library
+ private const WFD.CausalitySource s_CausalitySource = WFD.CausalitySource.Library;
+
+ //Lazy initialize the actual factory
+ private static WFD.IAsyncCausalityTracerStatics s_TracerFactory;
+
+ // The loggers that this Tracer knows about.
+ [Flags]
+ private enum Loggers : byte {
+ CausalityTracer = 1,
+ ETW = 2
+ }
+
+
+ //We receive the actual value for these as a callback
+ private static Loggers f_LoggingOn; //assumes false by default
+
+ // The precise static constructor will run first time somebody attempts to access this class
+ [SecuritySafeCritical]
+ static AsyncCausalityTracer()
+ {
+ if (!Environment.IsWinRTSupported) return;
+
+ //COM Class Id
+ string ClassId = "Windows.Foundation.Diagnostics.AsyncCausalityTracer";
+
+ //COM Interface GUID {50850B26-267E-451B-A890-AB6A370245EE}
+ Guid guid = new Guid(0x50850B26, 0x267E, 0x451B, 0xA8, 0x90, 0XAB, 0x6A, 0x37, 0x02, 0x45, 0xEE);
+
+ Object factory = null;
+
+ try
+ {
+ int hresult = Microsoft.Win32.UnsafeNativeMethods.RoGetActivationFactory(ClassId, ref guid, out factory);
+
+ if (hresult < 0 || factory == null) return; //This prevents having an exception thrown in case IAsyncCausalityTracerStatics isn't registered.
+
+ s_TracerFactory = (WFD.IAsyncCausalityTracerStatics)factory;
+
+ EventRegistrationToken token = s_TracerFactory.add_TracingStatusChanged(new EventHandler<WFD.TracingStatusChangedEventArgs>(TracingStatusChangedHandler));
+ Contract.Assert(token != default(EventRegistrationToken), "EventRegistrationToken is null");
+ }
+ catch (Exception ex)
+ {
+ // Although catching generic Exception is not recommended, this file is one exception
+ // since we don't want to propagate any kind of exception to the user since all we are
+ // doing here depends on internal state.
+ LogAndDisable(ex);
+ }
+
+ }
+
+ [SecuritySafeCritical]
+ private static void TracingStatusChangedHandler(Object sender, WFD.TracingStatusChangedEventArgs args)
+ {
+ if (args.Enabled)
+ f_LoggingOn |= Loggers.CausalityTracer;
+ else
+ f_LoggingOn &= ~Loggers.CausalityTracer;
+ }
+#endif
+
+ //
+ // The TraceXXX methods should be called only if LoggingOn property returned true
+ //
+
+ [FriendAccessAllowed]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Tracking is slow path. Disable inlining for it.
+ internal static void TraceOperationCreation(CausalityTraceLevel traceLevel, int taskId, string operationName, ulong relatedContext)
+ {
+#if FEATURE_COMINTEROP
+ try
+ {
+ if ((f_LoggingOn & Loggers.ETW) != 0)
+ TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long) relatedContext);
+ if ((f_LoggingOn & Loggers.CausalityTracer) != 0)
+ s_TracerFactory.TraceOperationCreation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), operationName, relatedContext);
+ }
+ catch(Exception ex)
+ {
+ //view function comment
+ LogAndDisable(ex);
+ }
+#endif
+ }
+
+ [FriendAccessAllowed]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ internal static void TraceOperationCompletion(CausalityTraceLevel traceLevel, int taskId, AsyncCausalityStatus status)
+ {
+#if FEATURE_COMINTEROP
+ try
+ {
+ if ((f_LoggingOn & Loggers.ETW) != 0)
+ TplEtwProvider.Log.TraceOperationEnd(taskId, status);
+ if ((f_LoggingOn & Loggers.CausalityTracer) != 0)
+ s_TracerFactory.TraceOperationCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.AsyncCausalityStatus)status);
+ }
+ catch(Exception ex)
+ {
+ //view function comment
+ LogAndDisable(ex);
+ }
+#endif
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ internal static void TraceOperationRelation(CausalityTraceLevel traceLevel, int taskId, CausalityRelation relation)
+ {
+#if FEATURE_COMINTEROP
+ try
+ {
+ if ((f_LoggingOn & Loggers.ETW) != 0)
+ TplEtwProvider.Log.TraceOperationRelation(taskId, relation);
+ if ((f_LoggingOn & Loggers.CausalityTracer) != 0)
+ s_TracerFactory.TraceOperationRelation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalityRelation)relation);
+ }
+ catch(Exception ex)
+ {
+ //view function comment
+ LogAndDisable(ex);
+ }
+#endif
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ internal static void TraceSynchronousWorkStart(CausalityTraceLevel traceLevel, int taskId, CausalitySynchronousWork work)
+ {
+#if FEATURE_COMINTEROP
+ try
+ {
+ if ((f_LoggingOn & Loggers.ETW) != 0)
+ TplEtwProvider.Log.TraceSynchronousWorkBegin(taskId, work);
+ if ((f_LoggingOn & Loggers.CausalityTracer) != 0)
+ s_TracerFactory.TraceSynchronousWorkStart((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalitySynchronousWork)work);
+ }
+ catch(Exception ex)
+ {
+ //view function comment
+ LogAndDisable(ex);
+ }
+#endif
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ internal static void TraceSynchronousWorkCompletion(CausalityTraceLevel traceLevel, CausalitySynchronousWork work)
+ {
+#if FEATURE_COMINTEROP
+ try
+ {
+ if ((f_LoggingOn & Loggers.ETW) != 0)
+ TplEtwProvider.Log.TraceSynchronousWorkEnd(work);
+ if ((f_LoggingOn & Loggers.CausalityTracer) != 0)
+ s_TracerFactory.TraceSynchronousWorkCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, (WFD.CausalitySynchronousWork)work);
+ }
+ catch(Exception ex)
+ {
+ //view function comment
+ LogAndDisable(ex);
+ }
+#endif
+ }
+
+#if FEATURE_COMINTEROP
+ //fix for 796185: leaking internal exceptions to customers,
+ //we should catch and log exceptions but never propagate them.
+ private static void LogAndDisable(Exception ex)
+ {
+ f_LoggingOn = 0;
+ Debugger.Log(0, "AsyncCausalityTracer", ex.ToString());
+ }
+#endif
+
+ private static ulong GetOperationId(uint taskId)
+ {
+ return (((ulong)AppDomain.CurrentDomain.Id) << 32) + taskId;
+ }
+
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs b/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs
new file mode 100644
index 0000000000..05e6dbf1a9
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs
@@ -0,0 +1,157 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Security;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Threading.Tasks {
+
+/// <summary>
+/// Provides an adapter to make Begin/End pairs awaitable.
+/// In general, Task.Factory.FromAsync should be used for this purpose.
+/// However, for cases where absolute minimal overhead is required, this type
+/// may be used to making APM pairs awaitable while minimizing overhead.
+/// (APM = Asynchronous Programming Model or the Begin/End pattern.)
+/// </summary>
+/// <remarks>
+/// This instance may be reused repeatedly. However, it must only be used
+/// by a single APM invocation at a time. It's state will automatically be reset
+/// when the await completes.
+/// </remarks>
+/// <example>
+/// Usage sample:
+/// <code>
+/// static async Task CopyStreamAsync(Stream source, Stream dest) {
+///
+/// BeginEndAwaitableAdapter adapter = new BeginEndAwaitableAdapter();
+/// Byte[] buffer = new Byte[0x1000];
+///
+/// while (true) {
+///
+/// source.BeginRead(buffer, 0, buffer.Length, BeginEndAwaitableAdapter.Callback, adapter);
+/// Int32 numRead = source.EndRead(await adapter);
+/// if (numRead == 0)
+/// break;
+///
+/// dest.BeginWrite(buffer, 0, numRead, BeginEndAwaitableAdapter.Callback, adapter);
+/// dest.EndWrite(await adapter);
+/// }
+/// }
+/// </code>
+/// </example>
+internal sealed class BeginEndAwaitableAdapter : ICriticalNotifyCompletion {
+
+ /// <summary>A sentinel marker used to communicate between OnCompleted and the APM callback
+ /// that the callback has already run, and thus OnCompleted needs to execute the callback.</summary>
+ private readonly static Action CALLBACK_RAN = () => { };
+
+ /// <summary>The IAsyncResult for the APM operation.</summary>
+ private IAsyncResult _asyncResult;
+
+ /// <summary>The continuation delegate provided to the awaiter.</summary>
+ private Action _continuation;
+
+
+ /// <summary>A callback to be passed as the AsyncCallback to an APM pair.
+ /// It expects that an BeginEndAwaitableAdapter instance was supplied to the APM Begin method as the object state.</summary>
+ public readonly static AsyncCallback Callback = (asyncResult) => {
+
+ Contract.Assert(asyncResult != null);
+ Contract.Assert(asyncResult.IsCompleted);
+ Contract.Assert(asyncResult.AsyncState is BeginEndAwaitableAdapter);
+
+ // Get the adapter object supplied as the "object state" to the Begin method
+ BeginEndAwaitableAdapter adapter = (BeginEndAwaitableAdapter) asyncResult.AsyncState;
+
+ // Store the IAsyncResult into it so that it's available to the awaiter
+ adapter._asyncResult = asyncResult;
+
+ // If the _continuation has already been set to the actual continuation by OnCompleted, then invoke the continuation.
+ // Set _continuation to the CALLBACK_RAN sentinel so that IsCompleted returns true and OnCompleted sees the sentinel
+ // and knows to invoke the callback.
+ // Due to some known incorrect implementations of IAsyncResult in the Framework where CompletedSynchronously is lazily
+ // set to true if it is first invoked after IsCompleted is true, we cannot rely here on CompletedSynchronously for
+ // synchronization between the caller and the callback, and thus do not use CompletedSynchronously at all.
+ Action continuation = Interlocked.Exchange(ref adapter._continuation, CALLBACK_RAN);
+ if (continuation != null) {
+
+ Contract.Assert(continuation != CALLBACK_RAN);
+ continuation();
+ }
+ };
+
+
+ /// <summary>Gets an awaiter.</summary>
+ /// <returns>Returns itself as the awaiter.</returns>
+ public BeginEndAwaitableAdapter GetAwaiter() {
+
+ return this;
+ }
+
+
+ /// <summary>Gets whether the awaited APM operation completed.</summary>
+ public bool IsCompleted {
+ get {
+
+ // We are completed if the callback was called and it set the continuation to the CALLBACK_RAN sentinel.
+ // If the operation completes asynchronously, there's still a chance we'll see CALLBACK_RAN here, in which
+ // case we're still good to keep running synchronously.
+ return (_continuation == CALLBACK_RAN);
+ }
+ }
+
+ /// <summary>Schedules the continuation to run when the operation completes.</summary>
+ /// <param name="continuation">The continuation.</param>
+ [SecurityCritical]
+ public void UnsafeOnCompleted(Action continuation) {
+
+ Contract.Assert(continuation != null);
+ OnCompleted(continuation);
+ }
+
+
+ /// <summary>Schedules the continuation to run when the operation completes.</summary>
+ /// <param name="continuation">The continuation.</param>
+ public void OnCompleted(Action continuation) {
+
+ Contract.Assert(continuation != null);
+
+ // If the continuation field is null, then set it to be the target continuation
+ // so that when the operation completes, it'll invoke the continuation. If it's non-null,
+ // it was already set to the CALLBACK_RAN-sentinel by the Callback, in which case we hit a very rare race condition
+ // where the operation didn't complete synchronously but completed asynchronously between our
+ // calls to IsCompleted and OnCompleted... in that case, just schedule a task to run the continuation.
+ if (_continuation == CALLBACK_RAN
+ || Interlocked.CompareExchange(ref _continuation, continuation, null) == CALLBACK_RAN) {
+
+ Task.Run(continuation); // must run async at this point, or else we'd risk stack diving
+ }
+ }
+
+
+ /// <summary>Gets the IAsyncResult for the APM operation after the operation completes, and then resets the adapter.</summary>
+ /// <returns>The IAsyncResult for the operation.</returns>
+ public IAsyncResult GetResult() {
+
+ Contract.Assert(_asyncResult != null && _asyncResult.IsCompleted);
+
+ // Get the IAsyncResult
+ IAsyncResult result = _asyncResult;
+
+ // Reset the adapter
+ _asyncResult = null;
+ _continuation = null;
+
+ // Return the result
+ return result;
+ }
+
+} // class BeginEndAwaitableAdapter
+
+} // namespace
diff --git a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
new file mode 100644
index 0000000000..cb4a22bb2b
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
@@ -0,0 +1,795 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A pair of schedulers that together support concurrent (reader) / exclusive (writer)
+// task scheduling. Using just the exclusive scheduler can be used to simulate a serial
+// processing queue, and using just the concurrent scheduler with a specified
+// MaximumConcurrentlyLevel can be used to achieve a MaxDegreeOfParallelism across
+// a bunch of tasks, parallel loops, dataflow blocks, etc.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Security;
+using System.Security.Permissions;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides concurrent and exclusive task schedulers that coordinate to execute
+ /// tasks while ensuring that concurrent tasks may run concurrently and exclusive tasks never do.
+ /// </summary>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerDisplay("Concurrent={ConcurrentTaskCountForDebugger}, Exclusive={ExclusiveTaskCountForDebugger}, Mode={ModeForDebugger}")]
+ [DebuggerTypeProxy(typeof(ConcurrentExclusiveSchedulerPair.DebugView))]
+ public class ConcurrentExclusiveSchedulerPair
+ {
+ /// <summary>A dictionary mapping thread ID to a processing mode to denote what kinds of tasks are currently being processed on this thread.</summary>
+ private readonly ConcurrentDictionary<int, ProcessingMode> m_threadProcessingMapping = new ConcurrentDictionary<int, ProcessingMode>();
+ /// <summary>The scheduler used to queue and execute "concurrent" tasks that may run concurrently with other concurrent tasks.</summary>
+ private readonly ConcurrentExclusiveTaskScheduler m_concurrentTaskScheduler;
+ /// <summary>The scheduler used to queue and execute "exclusive" tasks that must run exclusively while no other tasks for this pair are running.</summary>
+ private readonly ConcurrentExclusiveTaskScheduler m_exclusiveTaskScheduler;
+ /// <summary>The underlying task scheduler to which all work should be scheduled.</summary>
+ private readonly TaskScheduler m_underlyingTaskScheduler;
+ /// <summary>
+ /// The maximum number of tasks allowed to run concurrently. This only applies to concurrent tasks,
+ /// since exlusive tasks are inherently limited to 1.
+ /// </summary>
+ private readonly int m_maxConcurrencyLevel;
+ /// <summary>The maximum number of tasks we can process before recyling our runner tasks.</summary>
+ private readonly int m_maxItemsPerTask;
+ /// <summary>
+ /// If positive, it represents the number of concurrently running concurrent tasks.
+ /// If negative, it means an exclusive task has been scheduled.
+ /// If 0, nothing has been scheduled.
+ /// </summary>
+ private int m_processingCount;
+ /// <summary>Completion state for a task representing the completion of this pair.</summary>
+ /// <remarks>Lazily-initialized only if the scheduler pair is shutting down or if the Completion is requested.</remarks>
+ private CompletionState m_completionState;
+
+ /// <summary>A constant value used to signal unlimited processing.</summary>
+ private const int UNLIMITED_PROCESSING = -1;
+ /// <summary>Constant used for m_processingCount to indicate that an exclusive task is being processed.</summary>
+ private const int EXCLUSIVE_PROCESSING_SENTINEL = -1;
+ /// <summary>Default MaxItemsPerTask to use for processing if none is specified.</summary>
+ private const int DEFAULT_MAXITEMSPERTASK = UNLIMITED_PROCESSING;
+ /// <summary>Default MaxConcurrencyLevel is the processor count if not otherwise specified.</summary>
+ private static Int32 DefaultMaxConcurrencyLevel { get { return Environment.ProcessorCount; } }
+
+ /// <summary>Gets the sync obj used to protect all state on this instance.</summary>
+ private object ValueLock { get { return m_threadProcessingMapping; } }
+
+ /// <summary>
+ /// Initializes the ConcurrentExclusiveSchedulerPair.
+ /// </summary>
+ public ConcurrentExclusiveSchedulerPair() :
+ this(TaskScheduler.Default, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { }
+
+ /// <summary>
+ /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler.
+ /// </summary>
+ /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param>
+ public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler) :
+ this(taskScheduler, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { }
+
+ /// <summary>
+ /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum concurrency level.
+ /// </summary>
+ /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param>
+ /// <param name="maxConcurrencyLevel">The maximum number of tasks to run concurrently.</param>
+ public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel) :
+ this(taskScheduler, maxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { }
+
+ /// <summary>
+ /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum
+ /// concurrency level and a maximum number of scheduled tasks that may be processed as a unit.
+ /// </summary>
+ /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param>
+ /// <param name="maxConcurrencyLevel">The maximum number of tasks to run concurrently.</param>
+ /// <param name="maxItemsPerTask">The maximum number of tasks to process for each underlying scheduled task used by the pair.</param>
+ public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel, int maxItemsPerTask)
+ {
+ // Validate arguments
+ if (taskScheduler == null) throw new ArgumentNullException("taskScheduler");
+ if (maxConcurrencyLevel == 0 || maxConcurrencyLevel < -1) throw new ArgumentOutOfRangeException("maxConcurrencyLevel");
+ if (maxItemsPerTask == 0 || maxItemsPerTask < -1) throw new ArgumentOutOfRangeException("maxItemsPerTask");
+ Contract.EndContractBlock();
+
+ // Store configuration
+ m_underlyingTaskScheduler = taskScheduler;
+ m_maxConcurrencyLevel = maxConcurrencyLevel;
+ m_maxItemsPerTask = maxItemsPerTask;
+
+ // Downgrade to the underlying scheduler's max degree of parallelism if it's lower than the user-supplied level
+ int mcl = taskScheduler.MaximumConcurrencyLevel;
+ if (mcl > 0 && mcl < m_maxConcurrencyLevel) m_maxConcurrencyLevel = mcl;
+
+ // Treat UNLIMITED_PROCESSING/-1 for both MCL and MIPT as the biggest possible value so that we don't
+ // have to special case UNLIMITED_PROCESSING later on in processing.
+ if (m_maxConcurrencyLevel == UNLIMITED_PROCESSING) m_maxConcurrencyLevel = Int32.MaxValue;
+ if (m_maxItemsPerTask == UNLIMITED_PROCESSING) m_maxItemsPerTask = Int32.MaxValue;
+
+ // Create the concurrent/exclusive schedulers for this pair
+ m_exclusiveTaskScheduler = new ConcurrentExclusiveTaskScheduler(this, 1, ProcessingMode.ProcessingExclusiveTask);
+ m_concurrentTaskScheduler = new ConcurrentExclusiveTaskScheduler(this, m_maxConcurrencyLevel, ProcessingMode.ProcessingConcurrentTasks);
+ }
+
+ /// <summary>Informs the scheduler pair that it should not accept any more tasks.</summary>
+ /// <remarks>
+ /// Calling <see cref="Complete"/> is optional, and it's only necessary if the <see cref="Completion"/>
+ /// will be relied on for notification of all processing being completed.
+ /// </remarks>
+ public void Complete()
+ {
+ lock (ValueLock)
+ {
+ if (!CompletionRequested)
+ {
+ RequestCompletion();
+ CleanupStateIfCompletingAndQuiesced();
+ }
+ }
+ }
+
+ /// <summary>Gets a <see cref="System.Threading.Tasks.Task"/> that will complete when the scheduler has completed processing.</summary>
+ public Task Completion
+ {
+ // ValueLock not needed, but it's ok if it's held
+ get { return EnsureCompletionStateInitialized().Task; }
+ }
+
+ /// <summary>Gets the lazily-initialized completion state.</summary>
+ private CompletionState EnsureCompletionStateInitialized()
+ {
+ // ValueLock not needed, but it's ok if it's held
+ return LazyInitializer.EnsureInitialized(ref m_completionState, () => new CompletionState());
+ }
+
+ /// <summary>Gets whether completion has been requested.</summary>
+ private bool CompletionRequested
+ {
+ // ValueLock not needed, but it's ok if it's held
+ get { return m_completionState != null && Volatile.Read(ref m_completionState.m_completionRequested); }
+ }
+
+ /// <summary>Sets that completion has been requested.</summary>
+ private void RequestCompletion()
+ {
+ ContractAssertMonitorStatus(ValueLock, held: true);
+ EnsureCompletionStateInitialized().m_completionRequested = true;
+ }
+
+ /// <summary>
+ /// Cleans up state if and only if there's no processing currently happening
+ /// and no more to be done later.
+ /// </summary>
+ private void CleanupStateIfCompletingAndQuiesced()
+ {
+ ContractAssertMonitorStatus(ValueLock, held: true);
+ if (ReadyToComplete) CompleteTaskAsync();
+ }
+
+ /// <summary>Gets whether the pair is ready to complete.</summary>
+ private bool ReadyToComplete
+ {
+ get
+ {
+ ContractAssertMonitorStatus(ValueLock, held: true);
+
+ // We can only complete if completion has been requested and no processing is currently happening.
+ if (!CompletionRequested || m_processingCount != 0) return false;
+
+ // Now, only allow shutdown if an exception occurred or if there are no more tasks to process.
+ var cs = EnsureCompletionStateInitialized();
+ return
+ (cs.m_exceptions != null && cs.m_exceptions.Count > 0) ||
+ (m_concurrentTaskScheduler.m_tasks.IsEmpty && m_exclusiveTaskScheduler.m_tasks.IsEmpty);
+ }
+ }
+
+ /// <summary>Completes the completion task asynchronously.</summary>
+ private void CompleteTaskAsync()
+ {
+ Contract.Requires(ReadyToComplete, "The block must be ready to complete to be here.");
+ ContractAssertMonitorStatus(ValueLock, held: true);
+
+ // Ensure we only try to complete once, then schedule completion
+ // in order to escape held locks and the caller's context
+ var cs = EnsureCompletionStateInitialized();
+ if (!cs.m_completionQueued)
+ {
+ cs.m_completionQueued = true;
+ ThreadPool.QueueUserWorkItem(state =>
+ {
+ var localCs = (CompletionState)state; // don't use 'cs', as it'll force a closure
+ Contract.Assert(!localCs.Task.IsCompleted, "Completion should only happen once.");
+
+ var exceptions = localCs.m_exceptions;
+ bool success = (exceptions != null && exceptions.Count > 0) ?
+ localCs.TrySetException(exceptions) :
+ localCs.TrySetResult(default(VoidTaskResult));
+ Contract.Assert(success, "Expected to complete completion task.");
+ }, cs);
+ }
+ }
+
+ /// <summary>Initiatites scheduler shutdown due to a worker task faulting..</summary>
+ /// <param name="faultedTask">The faulted worker task that's initiating the shutdown.</param>
+ private void FaultWithTask(Task faultedTask)
+ {
+ Contract.Requires(faultedTask != null && faultedTask.IsFaulted && faultedTask.Exception.InnerExceptions.Count > 0,
+ "Needs a task in the faulted state and thus with exceptions.");
+ ContractAssertMonitorStatus(ValueLock, held: true);
+
+ // Store the faulted task's exceptions
+ var cs = EnsureCompletionStateInitialized();
+ if (cs.m_exceptions == null) cs.m_exceptions = new List<Exception>();
+ cs.m_exceptions.AddRange(faultedTask.Exception.InnerExceptions);
+
+ // Now that we're doomed, request completion
+ RequestCompletion();
+ }
+
+ /// <summary>
+ /// Gets a TaskScheduler that can be used to schedule tasks to this pair
+ /// that may run concurrently with other tasks on this pair.
+ /// </summary>
+ public TaskScheduler ConcurrentScheduler { get { return m_concurrentTaskScheduler; } }
+ /// <summary>
+ /// Gets a TaskScheduler that can be used to schedule tasks to this pair
+ /// that must run exclusively with regards to other tasks on this pair.
+ /// </summary>
+ public TaskScheduler ExclusiveScheduler { get { return m_exclusiveTaskScheduler; } }
+
+ /// <summary>Gets the number of tasks waiting to run concurrently.</summary>
+ /// <remarks>This does not take the necessary lock, as it's only called from under the debugger.</remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ private int ConcurrentTaskCountForDebugger { get { return m_concurrentTaskScheduler.m_tasks.Count; } }
+
+ /// <summary>Gets the number of tasks waiting to run exclusively.</summary>
+ /// <remarks>This does not take the necessary lock, as it's only called from under the debugger.</remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ private int ExclusiveTaskCountForDebugger { get { return m_exclusiveTaskScheduler.m_tasks.Count; } }
+
+ /// <summary>Notifies the pair that new work has arrived to be processed.</summary>
+ /// <param name="fairly">Whether tasks should be scheduled fairly with regards to other tasks.</param>
+ /// <remarks>Must only be called while holding the lock.</remarks>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
+ [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+ private void ProcessAsyncIfNecessary(bool fairly = false)
+ {
+ ContractAssertMonitorStatus(ValueLock, held: true);
+
+ // If the current processing count is >= 0, we can potentially launch further processing.
+ if (m_processingCount >= 0)
+ {
+ // We snap whether there are any exclusive tasks or concurrent tasks waiting.
+ // (We grab the concurrent count below only once we know we need it.)
+ // With processing happening concurrent to this operation, this data may
+ // immediately be out of date, but it can only go from non-empty
+ // to empty and not the other way around. As such, this is safe,
+ // as worst case is we'll schedule an extra task when we didn't
+ // otherwise need to, and we'll just eat its overhead.
+ bool exclusiveTasksAreWaiting = !m_exclusiveTaskScheduler.m_tasks.IsEmpty;
+
+ // If there's no processing currently happening but there are waiting exclusive tasks,
+ // let's start processing those exclusive tasks.
+ Task processingTask = null;
+ if (m_processingCount == 0 && exclusiveTasksAreWaiting)
+ {
+ // Launch exclusive task processing
+ m_processingCount = EXCLUSIVE_PROCESSING_SENTINEL; // -1
+ try
+ {
+ processingTask = new Task(thisPair => ((ConcurrentExclusiveSchedulerPair)thisPair).ProcessExclusiveTasks(), this,
+ default(CancellationToken), GetCreationOptionsForTask(fairly));
+ processingTask.Start(m_underlyingTaskScheduler);
+ // When we call Start, if the underlying scheduler throws in QueueTask, TPL will fault the task and rethrow
+ // the exception. To deal with that, we need a reference to the task object, so that we can observe its exception.
+ // Hence, we separate creation and starting, so that we can store a reference to the task before we attempt QueueTask.
+ }
+ catch
+ {
+ m_processingCount = 0;
+ FaultWithTask(processingTask);
+ }
+ }
+ // If there are no waiting exclusive tasks, there are concurrent tasks, and we haven't reached our maximum
+ // concurrency level for processing, let's start processing more concurrent tasks.
+ else
+ {
+ int concurrentTasksWaitingCount = m_concurrentTaskScheduler.m_tasks.Count;
+
+ if (concurrentTasksWaitingCount > 0 && !exclusiveTasksAreWaiting && m_processingCount < m_maxConcurrencyLevel)
+ {
+ // Launch concurrent task processing, up to the allowed limit
+ for (int i = 0; i < concurrentTasksWaitingCount && m_processingCount < m_maxConcurrencyLevel; ++i)
+ {
+ ++m_processingCount;
+ try
+ {
+ processingTask = new Task(thisPair => ((ConcurrentExclusiveSchedulerPair)thisPair).ProcessConcurrentTasks(), this,
+ default(CancellationToken), GetCreationOptionsForTask(fairly));
+ processingTask.Start(m_underlyingTaskScheduler); // See above logic for why we use new + Start rather than StartNew
+ }
+ catch
+ {
+ --m_processingCount;
+ FaultWithTask(processingTask);
+ }
+ }
+ }
+ }
+
+ // Check to see if all tasks have completed and if completion has been requested.
+ CleanupStateIfCompletingAndQuiesced();
+ }
+ else Contract.Assert(m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL, "The processing count must be the sentinel if it's not >= 0.");
+ }
+
+ /// <summary>
+ /// Processes exclusive tasks serially until either there are no more to process
+ /// or we've reached our user-specified maximum limit.
+ /// </summary>
+ private void ProcessExclusiveTasks()
+ {
+ Contract.Requires(m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL, "Processing exclusive tasks requires being in exclusive mode.");
+ Contract.Requires(!m_exclusiveTaskScheduler.m_tasks.IsEmpty, "Processing exclusive tasks requires tasks to be processed.");
+ ContractAssertMonitorStatus(ValueLock, held: false);
+ try
+ {
+ // Note that we're processing exclusive tasks on the current thread
+ Contract.Assert(!m_threadProcessingMapping.ContainsKey(Thread.CurrentThread.ManagedThreadId),
+ "This thread should not yet be involved in this pair's processing.");
+ m_threadProcessingMapping[Thread.CurrentThread.ManagedThreadId] = ProcessingMode.ProcessingExclusiveTask;
+
+ // Process up to the maximum number of items per task allowed
+ for (int i = 0; i < m_maxItemsPerTask; i++)
+ {
+ // Get the next available exclusive task. If we can't find one, bail.
+ Task exclusiveTask;
+ if (!m_exclusiveTaskScheduler.m_tasks.TryDequeue(out exclusiveTask)) break;
+
+ // Execute the task. If the scheduler was previously faulted,
+ // this task could have been faulted when it was queued; ignore such tasks.
+ if (!exclusiveTask.IsFaulted) m_exclusiveTaskScheduler.ExecuteTask(exclusiveTask);
+ }
+ }
+ finally
+ {
+ // We're no longer processing exclusive tasks on the current thread
+ ProcessingMode currentMode;
+ m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode);
+ Contract.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask,
+ "Somehow we ended up escaping exclusive mode.");
+
+ lock (ValueLock)
+ {
+ // When this task was launched, we tracked it by setting m_processingCount to WRITER_IN_PROGRESS.
+ // now reset it to 0. Then check to see whether there's more processing to be done.
+ // There might be more concurrent tasks available, for example, if concurrent tasks arrived
+ // after we exited the loop, or if we exited the loop while concurrent tasks were still
+ // available but we hit our maxItemsPerTask limit.
+ Contract.Assert(m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL, "The processing mode should not have deviated from exclusive.");
+ m_processingCount = 0;
+ ProcessAsyncIfNecessary(true);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Processes concurrent tasks serially until either there are no more to process,
+ /// we've reached our user-specified maximum limit, or exclusive tasks have arrived.
+ /// </summary>
+ private void ProcessConcurrentTasks()
+ {
+ Contract.Requires(m_processingCount > 0, "Processing concurrent tasks requires us to be in concurrent mode.");
+ ContractAssertMonitorStatus(ValueLock, held: false);
+ try
+ {
+ // Note that we're processing concurrent tasks on the current thread
+ Contract.Assert(!m_threadProcessingMapping.ContainsKey(Thread.CurrentThread.ManagedThreadId),
+ "This thread should not yet be involved in this pair's processing.");
+ m_threadProcessingMapping[Thread.CurrentThread.ManagedThreadId] = ProcessingMode.ProcessingConcurrentTasks;
+
+ // Process up to the maximum number of items per task allowed
+ for (int i = 0; i < m_maxItemsPerTask; i++)
+ {
+ // Get the next available concurrent task. If we can't find one, bail.
+ Task concurrentTask;
+ if (!m_concurrentTaskScheduler.m_tasks.TryDequeue(out concurrentTask)) break;
+
+ // Execute the task. If the scheduler was previously faulted,
+ // this task could have been faulted when it was queued; ignore such tasks.
+ if (!concurrentTask.IsFaulted) m_concurrentTaskScheduler.ExecuteTask(concurrentTask);
+
+ // Now check to see if exclusive tasks have arrived; if any have, they take priority
+ // so we'll bail out here. Note that we could have checked this condition
+ // in the for loop's condition, but that could lead to extra overhead
+ // in the case where a concurrent task arrives, this task is launched, and then
+ // before entering the loop an exclusive task arrives. If we didn't execute at
+ // least one task, we would have spent all of the overhead to launch a
+ // task but with none of the benefit. There's of course also an inherent
+ // race condition here with regards to exclusive tasks arriving, and we're ok with
+ // executing one more concurrent task than we should before giving priority to exclusive tasks.
+ if (!m_exclusiveTaskScheduler.m_tasks.IsEmpty) break;
+ }
+ }
+ finally
+ {
+ // We're no longer processing concurrent tasks on the current thread
+ ProcessingMode currentMode;
+ m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode);
+ Contract.Assert(currentMode == ProcessingMode.ProcessingConcurrentTasks,
+ "Somehow we ended up escaping concurrent mode.");
+
+ lock (ValueLock)
+ {
+ // When this task was launched, we tracked it with a positive processing count;
+ // decrement that count. Then check to see whether there's more processing to be done.
+ // There might be more concurrent tasks available, for example, if concurrent tasks arrived
+ // after we exited the loop, or if we exited the loop while concurrent tasks were still
+ // available but we hit our maxItemsPerTask limit.
+ Contract.Assert(m_processingCount > 0, "The procesing mode should not have deviated from concurrent.");
+ if (m_processingCount > 0) --m_processingCount;
+ ProcessAsyncIfNecessary(true);
+ }
+ }
+ }
+
+#if PRENET45
+ /// <summary>
+ /// Type used with TaskCompletionSource(Of TResult) as the TResult
+ /// to ensure that the resulting task can't be upcast to something
+ /// that in the future could lead to compat problems.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
+ [DebuggerNonUserCode]
+ private struct VoidTaskResult { }
+#endif
+
+ /// <summary>
+ /// Holder for lazily-initialized state about the completion of a scheduler pair.
+ /// Completion is only triggered either by rare exceptional conditions or by
+ /// the user calling Complete, and as such we only lazily initialize this
+ /// state in one of those conditions or if the user explicitly asks for
+ /// the Completion.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
+ private sealed class CompletionState : TaskCompletionSource<VoidTaskResult>
+ {
+ /// <summary>Whether the scheduler has had completion requested.</summary>
+ /// <remarks>This variable is not volatile, so to gurantee safe reading reads, Volatile.Read is used in TryExecuteTaskInline.</remarks>
+ internal bool m_completionRequested;
+ /// <summary>Whether completion processing has been queued.</summary>
+ internal bool m_completionQueued;
+ /// <summary>Unrecoverable exceptions incurred while processing.</summary>
+ internal List<Exception> m_exceptions;
+ }
+
+ /// <summary>
+ /// A scheduler shim used to queue tasks to the pair and execute those tasks on request of the pair.
+ /// </summary>
+ [DebuggerDisplay("Count={CountForDebugger}, MaxConcurrencyLevel={m_maxConcurrencyLevel}, Id={Id}")]
+ [DebuggerTypeProxy(typeof(ConcurrentExclusiveTaskScheduler.DebugView))]
+ private sealed class ConcurrentExclusiveTaskScheduler : TaskScheduler
+ {
+ /// <summary>Cached delegate for invoking TryExecuteTaskShim.</summary>
+ private static readonly Func<object, bool> s_tryExecuteTaskShim = new Func<object, bool>(TryExecuteTaskShim);
+ /// <summary>The parent pair.</summary>
+ private readonly ConcurrentExclusiveSchedulerPair m_pair;
+ /// <summary>The maximum concurrency level for the scheduler.</summary>
+ private readonly int m_maxConcurrencyLevel;
+ /// <summary>The processing mode of this scheduler, exclusive or concurrent.</summary>
+ private readonly ProcessingMode m_processingMode;
+ /// <summary>Gets the queue of tasks for this scheduler.</summary>
+ internal readonly IProducerConsumerQueue<Task> m_tasks;
+
+ /// <summary>Initializes the scheduler.</summary>
+ /// <param name="pair">The parent pair.</param>
+ /// <param name="maxConcurrencyLevel">The maximum degree of concurrency this scheduler may use.</param>
+ /// <param name="processingMode">The processing mode of this scheduler.</param>
+ internal ConcurrentExclusiveTaskScheduler(ConcurrentExclusiveSchedulerPair pair, int maxConcurrencyLevel, ProcessingMode processingMode)
+ {
+ Contract.Requires(pair != null, "Scheduler must be associated with a valid pair.");
+ Contract.Requires(processingMode == ProcessingMode.ProcessingConcurrentTasks || processingMode == ProcessingMode.ProcessingExclusiveTask,
+ "Scheduler must be for concurrent or exclusive processing.");
+ Contract.Requires(
+ (processingMode == ProcessingMode.ProcessingConcurrentTasks && (maxConcurrencyLevel >= 1 || maxConcurrencyLevel == UNLIMITED_PROCESSING)) ||
+ (processingMode == ProcessingMode.ProcessingExclusiveTask && maxConcurrencyLevel == 1),
+ "If we're in concurrent mode, our concurrency level should be positive or unlimited. If exclusive, it should be 1.");
+
+ m_pair = pair;
+ m_maxConcurrencyLevel = maxConcurrencyLevel;
+ m_processingMode = processingMode;
+ m_tasks = (processingMode == ProcessingMode.ProcessingExclusiveTask) ?
+ (IProducerConsumerQueue<Task>)new SingleProducerSingleConsumerQueue<Task>() :
+ (IProducerConsumerQueue<Task>)new MultiProducerMultiConsumerQueue<Task>();
+ }
+
+ /// <summary>Gets the maximum concurrency level this scheduler is able to support.</summary>
+ public override int MaximumConcurrencyLevel { get { return m_maxConcurrencyLevel; } }
+
+ /// <summary>Queues a task to the scheduler.</summary>
+ /// <param name="task">The task to be queued.</param>
+ [SecurityCritical]
+ protected internal override void QueueTask(Task task)
+ {
+ Contract.Assert(task != null, "Infrastructure should have provided a non-null task.");
+ lock (m_pair.ValueLock)
+ {
+ // If the scheduler has already had completion requested, no new work is allowed to be scheduled
+ if (m_pair.CompletionRequested) throw new InvalidOperationException(GetType().Name);
+
+ // Queue the task, and then let the pair know that more work is now available to be scheduled
+ m_tasks.Enqueue(task);
+ m_pair.ProcessAsyncIfNecessary();
+ }
+ }
+
+ /// <summary>Executes a task on this scheduler.</summary>
+ /// <param name="task">The task to be executed.</param>
+ [SecuritySafeCritical]
+ internal void ExecuteTask(Task task)
+ {
+ Contract.Assert(task != null, "Infrastructure should have provided a non-null task.");
+ base.TryExecuteTask(task);
+ }
+
+ /// <summary>Tries to execute the task synchronously on this scheduler.</summary>
+ /// <param name="task">The task to execute.</param>
+ /// <param name="taskWasPreviouslyQueued">Whether the task was previously queued to the scheduler.</param>
+ /// <returns>true if the task could be executed; otherwise, false.</returns>
+ [SecurityCritical]
+ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
+ {
+ Contract.Assert(task != null, "Infrastructure should have provided a non-null task.");
+
+ // If the scheduler has had completion requested, no new work is allowed to be scheduled.
+ // A non-locked read on m_completionRequested (in CompletionRequested) is acceptable here because:
+ // a) we don't need to be exact... a Complete call could come in later in the function anyway
+ // b) this is only a fast path escape hatch. To actually inline the task,
+ // we need to be inside of an already executing task, and in such a case,
+ // while completion may have been requested, we can't have shutdown yet.
+ if (!taskWasPreviouslyQueued && m_pair.CompletionRequested) return false;
+
+ // We know the implementation of the default scheduler and how it will behave.
+ // As it's the most common underlying scheduler, we optimize for it.
+ bool isDefaultScheduler = m_pair.m_underlyingTaskScheduler == TaskScheduler.Default;
+
+ // If we're targeting the default scheduler and taskWasPreviouslyQueued is true,
+ // we know that the default scheduler will only allow it to be inlined
+ // if we're on a thread pool thread (but it won't always allow it in that case,
+ // since it'll only allow inlining if it can find the task in the local queue).
+ // As such, if we're not on a thread pool thread, we know for sure the
+ // task won't be inlined, so let's not even try.
+ if (isDefaultScheduler && taskWasPreviouslyQueued && !Thread.CurrentThread.IsThreadPoolThread)
+ {
+ return false;
+ }
+ else
+ {
+ // If a task is already running on this thread, allow inline execution to proceed.
+ // If there's already a task from this scheduler running on the current thread, we know it's safe
+ // to run this task, in effect temporarily taking that task's count allocation.
+ ProcessingMode currentThreadMode;
+ if (m_pair.m_threadProcessingMapping.TryGetValue(Thread.CurrentThread.ManagedThreadId, out currentThreadMode) &&
+ currentThreadMode == m_processingMode)
+ {
+ // If we're targeting the default scheduler and taskWasPreviouslyQueued is false,
+ // we know the default scheduler will allow it, so we can just execute it here.
+ // Otherwise, delegate to the target scheduler's inlining.
+ return (isDefaultScheduler && !taskWasPreviouslyQueued) ?
+ TryExecuteTask(task) :
+ TryExecuteTaskInlineOnTargetScheduler(task);
+ }
+ }
+
+ // We're not in the context of a task already executing on this scheduler. Bail.
+ return false;
+ }
+
+ /// <summary>
+ /// Implements a reasonable approximation for TryExecuteTaskInline on the underlying scheduler,
+ /// which we can't call directly on the underlying scheduler.
+ /// </summary>
+ /// <param name="task">The task to execute inline if possible.</param>
+ /// <returns>true if the task was inlined successfully; otherwise, false.</returns>
+ [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "ignored")]
+ private bool TryExecuteTaskInlineOnTargetScheduler(Task task)
+ {
+ // We'd like to simply call TryExecuteTaskInline here, but we can't.
+ // As there's no built-in API for this, a workaround is to create a new task that,
+ // when executed, will simply call TryExecuteTask to run the real task, and then
+ // we run our new shim task synchronously on the target scheduler. If all goes well,
+ // our synchronous invocation will succeed in running the shim task on the current thread,
+ // which will in turn run the real task on the current thread. If the scheduler
+ // doesn't allow that execution, RunSynchronously will block until the underlying scheduler
+ // is able to invoke the task, which might account for an additional but unavoidable delay.
+ // Once it's done, we can return whether the task executed by returning the
+ // shim task's Result, which is in turn the result of TryExecuteTask.
+ var t = new Task<bool>(s_tryExecuteTaskShim, Tuple.Create(this, task));
+ try
+ {
+ t.RunSynchronously(m_pair.m_underlyingTaskScheduler);
+ return t.Result;
+ }
+ catch
+ {
+ Contract.Assert(t.IsFaulted, "Task should be faulted due to the scheduler faulting it and throwing the exception.");
+ var ignored = t.Exception;
+ throw;
+ }
+ finally { t.Dispose(); }
+ }
+
+ /// <summary>Shim used to invoke this.TryExecuteTask(task).</summary>
+ /// <param name="state">A tuple of the ConcurrentExclusiveTaskScheduler and the task to execute.</param>
+ /// <returns>true if the task was successfully inlined; otherwise, false.</returns>
+ /// <remarks>
+ /// This method is separated out not because of performance reasons but so that
+ /// the SecuritySafeCritical attribute may be employed.
+ /// </remarks>
+ [SecuritySafeCritical]
+ private static bool TryExecuteTaskShim(object state)
+ {
+ var tuple = (Tuple<ConcurrentExclusiveTaskScheduler, Task>)state;
+ return tuple.Item1.TryExecuteTask(tuple.Item2);
+ }
+
+ /// <summary>Gets for debugging purposes the tasks scheduled to this scheduler.</summary>
+ /// <returns>An enumerable of the tasks queued.</returns>
+ [SecurityCritical]
+ protected override IEnumerable<Task> GetScheduledTasks() { return m_tasks; }
+
+ /// <summary>Gets the number of tasks queued to this scheduler.</summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ private int CountForDebugger { get { return m_tasks.Count; } }
+
+ /// <summary>Provides a debug view for ConcurrentExclusiveTaskScheduler.</summary>
+ private sealed class DebugView
+ {
+ /// <summary>The scheduler being debugged.</summary>
+ private readonly ConcurrentExclusiveTaskScheduler m_taskScheduler;
+
+ /// <summary>Initializes the debug view.</summary>
+ /// <param name="scheduler">The scheduler being debugged.</param>
+ public DebugView(ConcurrentExclusiveTaskScheduler scheduler)
+ {
+ Contract.Requires(scheduler != null, "Need a scheduler with which to construct the debug view.");
+ m_taskScheduler = scheduler;
+ }
+
+ /// <summary>Gets this pair's maximum allowed concurrency level.</summary>
+ public int MaximumConcurrencyLevel { get { return m_taskScheduler.m_maxConcurrencyLevel; } }
+ /// <summary>Gets the tasks scheduled to this scheduler.</summary>
+ public IEnumerable<Task> ScheduledTasks { get { return m_taskScheduler.m_tasks; } }
+ /// <summary>Gets the scheduler pair with which this scheduler is associated.</summary>
+ public ConcurrentExclusiveSchedulerPair SchedulerPair { get { return m_taskScheduler.m_pair; } }
+ }
+ }
+
+ /// <summary>Provides a debug view for ConcurrentExclusiveSchedulerPair.</summary>
+ private sealed class DebugView
+ {
+ /// <summary>The pair being debugged.</summary>
+ private readonly ConcurrentExclusiveSchedulerPair m_pair;
+
+ /// <summary>Initializes the debug view.</summary>
+ /// <param name="pair">The pair being debugged.</param>
+ public DebugView(ConcurrentExclusiveSchedulerPair pair)
+ {
+ Contract.Requires(pair != null, "Need a pair with which to construct the debug view.");
+ m_pair = pair;
+ }
+
+ /// <summary>Gets a representation of the execution state of the pair.</summary>
+ public ProcessingMode Mode { get { return m_pair.ModeForDebugger; } }
+ /// <summary>Gets the number of tasks waiting to run exclusively.</summary>
+ public IEnumerable<Task> ScheduledExclusive { get { return m_pair.m_exclusiveTaskScheduler.m_tasks; } }
+ /// <summary>Gets the number of tasks waiting to run concurrently.</summary>
+ public IEnumerable<Task> ScheduledConcurrent { get { return m_pair.m_concurrentTaskScheduler.m_tasks; } }
+ /// <summary>Gets the number of tasks currently being executed.</summary>
+ public int CurrentlyExecutingTaskCount
+ {
+ get { return (m_pair.m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL) ? 1 : m_pair.m_processingCount; }
+ }
+ /// <summary>Gets the underlying task scheduler that actually executes the tasks.</summary>
+ public TaskScheduler TargetScheduler { get { return m_pair.m_underlyingTaskScheduler; } }
+ }
+
+ /// <summary>Gets an enumeration for debugging that represents the current state of the scheduler pair.</summary>
+ /// <remarks>This is only for debugging. It does not take the necessary locks to be useful for runtime usage.</remarks>
+ private ProcessingMode ModeForDebugger
+ {
+ get
+ {
+ // If our completion task is done, so are we.
+ if (m_completionState != null && m_completionState.Task.IsCompleted) return ProcessingMode.Completed;
+
+ // Otherwise, summarize our current state.
+ var mode = ProcessingMode.NotCurrentlyProcessing;
+ if (m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL) mode |= ProcessingMode.ProcessingExclusiveTask;
+ if (m_processingCount >= 1) mode |= ProcessingMode.ProcessingConcurrentTasks;
+ if (CompletionRequested) mode |= ProcessingMode.Completing;
+ return mode;
+ }
+ }
+
+ /// <summary>Asserts that a given synchronization object is either held or not held.</summary>
+ /// <param name="syncObj">The monitor to check.</param>
+ /// <param name="held">Whether we want to assert that it's currently held or not held.</param>
+ [Conditional("DEBUG")]
+ internal static void ContractAssertMonitorStatus(object syncObj, bool held)
+ {
+ Contract.Requires(syncObj != null, "The monitor object to check must be provided.");
+#if PRENET45
+#if DEBUG
+ // This check is expensive,
+ // which is why it's protected by ShouldCheckMonitorStatus and controlled by an environment variable DEBUGSYNC.
+ if (ShouldCheckMonitorStatus)
+ {
+ bool exceptionThrown;
+ try
+ {
+ Monitor.Pulse(syncObj); // throws a SynchronizationLockException if the monitor isn't held by this thread
+ exceptionThrown = false;
+ }
+ catch (SynchronizationLockException) { exceptionThrown = true; }
+ Contract.Assert(held == !exceptionThrown, "The locking scheme was not correctly followed.");
+ }
+#endif
+#else
+ Contract.Assert(Monitor.IsEntered(syncObj) == held, "The locking scheme was not correctly followed.");
+#endif
+ }
+
+ /// <summary>Gets the options to use for tasks.</summary>
+ /// <param name="isReplacementReplica">If this task is being created to replace another.</param>
+ /// <remarks>
+ /// These options should be used for all tasks that have the potential to run user code or
+ /// that are repeatedly spawned and thus need a modicum of fair treatment.
+ /// </remarks>
+ /// <returns>The options to use.</returns>
+ internal static TaskCreationOptions GetCreationOptionsForTask(bool isReplacementReplica = false)
+ {
+ TaskCreationOptions options =
+#if PRENET45
+ TaskCreationOptions.None;
+#else
+ TaskCreationOptions.DenyChildAttach;
+#endif
+ if (isReplacementReplica) options |= TaskCreationOptions.PreferFairness;
+ return options;
+ }
+
+ /// <summary>Provides an enumeration that represents the current state of the scheduler pair.</summary>
+ [Flags]
+ private enum ProcessingMode : byte
+ {
+ /// <summary>The scheduler pair is currently dormant, with no work scheduled.</summary>
+ NotCurrentlyProcessing = 0x0,
+ /// <summary>The scheduler pair has queued processing for exclusive tasks.</summary>
+ ProcessingExclusiveTask = 0x1,
+ /// <summary>The scheduler pair has queued processing for concurrent tasks.</summary>
+ ProcessingConcurrentTasks = 0x2,
+ /// <summary>Completion has been requested.</summary>
+ Completing = 0x4,
+ /// <summary>The scheduler pair is finished processing.</summary>
+ Completed = 0x8
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs
new file mode 100644
index 0000000000..b1f634c707
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs
@@ -0,0 +1,2303 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// As with TaskFactory, TaskFactory<TResult> encodes common factory patterns into helper methods.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides support for creating and scheduling
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task{TResult}</see> objects.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the results that are available though
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}">Task{TResult}</see> objects that are associated with
+ /// the methods in this class.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// There are many common patterns for which tasks are relevant. The <see cref="TaskFactory{TResult}"/>
+ /// class encodes some of these patterns into methods that pick up default settings, which are
+ /// configurable through its constructors.
+ /// </para>
+ /// <para>
+ /// A default instance of <see cref="TaskFactory{TResult}"/> is available through the
+ /// <see cref="System.Threading.Tasks.Task{TResult}.Factory">Task{TResult}.Factory</see> property.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class TaskFactory<TResult>
+ {
+ // Member variables, DefaultScheduler, other properties and ctors
+ // copied right out of TaskFactory... Lots of duplication here...
+ // Should we be thinking about a TaskFactoryBase class?
+
+ // member variables
+ private CancellationToken m_defaultCancellationToken;
+ private TaskScheduler m_defaultScheduler;
+ private TaskCreationOptions m_defaultCreationOptions;
+ private TaskContinuationOptions m_defaultContinuationOptions;
+
+ private TaskScheduler DefaultScheduler
+ {
+ get
+ {
+ if (m_defaultScheduler == null) return TaskScheduler.Current;
+ else return m_defaultScheduler;
+ }
+ }
+
+ // sister method to above property -- avoids a TLS lookup
+ private TaskScheduler GetDefaultScheduler(Task currTask)
+ {
+ if (m_defaultScheduler != null) return m_defaultScheduler;
+ else if ((currTask != null)
+ && ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
+ )
+ return currTask.ExecutingTaskScheduler;
+ else return TaskScheduler.Default;
+ }
+
+ /* Constructors */
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the default configuration.
+ /// </summary>
+ /// <remarks>
+ /// This constructor creates a <see cref="TaskFactory{TResult}"/> instance with a default configuration. The
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory()
+ : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the default configuration.
+ /// </summary>
+ /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned
+ /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified
+ /// while calling the factory methods.</param>
+ /// <remarks>
+ /// This constructor creates a <see cref="TaskFactory{TResult}"/> instance with a default configuration. The
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(CancellationToken cancellationToken)
+ : this(cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="scheduler">
+ /// The <see cref="System.Threading.Tasks.TaskScheduler">
+ /// TaskScheduler</see> to use to schedule any tasks created with this TaskFactory{TResult}. A null value
+ /// indicates that the current TaskScheduler should be used.
+ /// </param>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to <paramref name="scheduler"/>, unless it's null, in which case the property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(TaskScheduler scheduler) // null means to use TaskScheduler.Current
+ : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, scheduler)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="creationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskCreationOptions">
+ /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory{TResult}.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory{TResult}.
+ /// </param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/>
+ /// argument specifies an invalid value.
+ /// </exception>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>,
+ /// the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref
+ /// name="continuationOptions"/>, and the <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to the
+ /// current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions)
+ : this(default(CancellationToken), creationOptions, continuationOptions, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned
+ /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified
+ /// while calling the factory methods.</param>
+ /// <param name="creationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskCreationOptions">
+ /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory{TResult}.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory{TResult}.
+ /// </param>
+ /// <param name="scheduler">
+ /// The default <see cref="System.Threading.Tasks.TaskScheduler">
+ /// TaskScheduler</see> to use to schedule any Tasks created with this TaskFactory{TResult}. A null value
+ /// indicates that TaskScheduler.Current should be used.
+ /// </param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/>
+ /// argumentspecifies an invalid value.
+ /// </exception>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>,
+ /// the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref
+ /// name="continuationOptions"/>, and the <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to
+ /// <paramref name="scheduler"/>, unless it's null, in which case the property is initialized to the
+ /// current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
+ TaskFactory.CheckCreationOptions(creationOptions);
+
+ m_defaultCancellationToken = cancellationToken;
+ m_defaultScheduler = scheduler;
+ m_defaultCreationOptions = creationOptions;
+ m_defaultContinuationOptions = continuationOptions;
+ }
+
+ /* Properties */
+
+ /// <summary>
+ /// Gets the default <see cref="System.Threading.CancellationToken">CancellationToken</see> of this
+ /// TaskFactory.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default <see cref="CancellationToken"/> that will be assigned to all
+ /// tasks created by this factory unless another CancellationToken value is explicitly specified
+ /// during the call to the factory methods.
+ /// </remarks>
+ public CancellationToken CancellationToken { get { return m_defaultCancellationToken; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> of this
+ /// TaskFactory{TResult}.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default scheduler for this factory. It will be used to schedule all
+ /// tasks unless another scheduler is explicitly specified during calls to this factory's methods.
+ /// If null, <see cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>
+ /// will be used.
+ /// </remarks>
+ public TaskScheduler Scheduler { get { return m_defaultScheduler; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions
+ /// </see> value of this TaskFactory{TResult}.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default creation options for this factory. They will be used to create all
+ /// tasks unless other options are explicitly specified during calls to this factory's methods.
+ /// </remarks>
+ public TaskCreationOptions CreationOptions { get { return m_defaultCreationOptions; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskContinuationOptions
+ /// </see> value of this TaskFactory{TResult}.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default continuation options for this factory. They will be used to create
+ /// all continuation tasks unless other options are explicitly specified during calls to this factory's methods.
+ /// </remarks>
+ public TaskContinuationOptions ContinuationOptions { get { return m_defaultContinuationOptions; } }
+
+
+ /* StartNew */
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<TResult> function)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<TResult> function, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, cancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<TResult> function, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken,
+ creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}">
+ /// Task{TResult}</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<TResult> function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(
+ Task.InternalCurrentIfAttached(creationOptions), function, cancellationToken,
+ creationOptions, InternalTaskOptions.None, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<Object, TResult> function, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<Object, TResult> function, Object state, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, cancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<Object, TResult> function, Object state, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken,
+ creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}">
+ /// Task{TResult}</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew(Func<Object, TResult> function, Object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(Task.InternalCurrentIfAttached(creationOptions), function, state, cancellationToken,
+ creationOptions, InternalTaskOptions.None, scheduler, ref stackMark);
+ }
+
+ //
+ // APM Factory methods
+ //
+
+ // Common core logic for FromAsync calls. This minimizes the chance of "drift" between overload implementations.
+ private static void FromAsyncCoreLogic(
+ IAsyncResult iar,
+ Func<IAsyncResult, TResult> endFunction,
+ Action<IAsyncResult> endAction,
+ Task<TResult> promise,
+ bool requiresSynchronization)
+ {
+ Contract.Requires((endFunction != null) != (endAction != null), "Expected exactly one of endFunction/endAction to be non-null");
+
+ Exception ex = null;
+ OperationCanceledException oce = null;
+ TResult result = default(TResult);
+
+ try
+ {
+ if (endFunction != null)
+ {
+ result = endFunction(iar);
+ }
+ else
+ {
+ endAction(iar);
+ }
+ }
+ catch (OperationCanceledException _oce) { oce = _oce; }
+ catch (Exception e) { ex = e; }
+ finally
+ {
+ if (oce != null)
+ {
+ promise.TrySetCanceled(oce.CancellationToken, oce);
+ }
+ else if (ex != null)
+ {
+ bool bWonSetException = promise.TrySetException(ex);
+ if (bWonSetException && ex is ThreadAbortException)
+ {
+ promise.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
+ }
+ }
+ else
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.RemoveFromActiveTasks(promise.Id);
+ }
+ if (requiresSynchronization)
+ {
+ promise.TrySetResult(result);
+ }
+ else
+ {
+ promise.DangerousSetResult(result);
+ }
+ }
+
+
+ }
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync(IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync(
+ IAsyncResult asyncResult,
+ Func<IAsyncResult, TResult> endMethod,
+ TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler, ref stackMark);
+ }
+
+
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the task that executes the end method.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync(
+ IAsyncResult asyncResult,
+ Func<IAsyncResult, TResult> endMethod,
+ TaskCreationOptions creationOptions,
+ TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler, ref stackMark);
+ }
+
+ // internal overload that supports StackCrawlMark
+ // We also need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
+ // method can access the logic w/o declaring a TaskFactory<TResult> instance.
+ internal static Task<TResult> FromAsyncImpl(
+ IAsyncResult asyncResult,
+ Func<IAsyncResult, TResult> endFunction,
+ Action<IAsyncResult> endAction,
+ TaskCreationOptions creationOptions,
+ TaskScheduler scheduler,
+ ref StackCrawlMark stackMark)
+ {
+ if (asyncResult == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult);
+
+ if (endFunction == null && endAction == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod);
+
+ Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
+
+ if (scheduler == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ Contract.EndContractBlock();
+
+ TaskFactory.CheckFromAsyncOptions(creationOptions, false);
+
+ Task<TResult> promise = new Task<TResult>((object)null, creationOptions);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(promise);
+ }
+
+ // Just specify this task as detached. No matter what happens, we want endMethod
+ // to be called -- even if the parent is canceled. So we don't want to flow
+ // RespectParentCancellation.
+ Task t = new Task(delegate
+ {
+ FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true);
+ },
+ (object)null, null,
+ default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null, ref stackMark);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Verbose, t.Id, "TaskFactory.FromAsync Callback", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(t);
+ }
+
+ if (asyncResult.IsCompleted)
+ {
+ try { t.InternalRunSynchronously(scheduler, waitForCompletion:false); }
+ catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions
+ }
+ else
+ {
+ ThreadPool.RegisterWaitForSingleObject(
+ asyncResult.AsyncWaitHandle,
+ delegate
+ {
+ try { t.InternalRunSynchronously(scheduler, waitForCompletion: false); }
+ catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions
+ },
+ null,
+ Timeout.Infinite,
+ true);
+ }
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, object state)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, object state, TaskCreationOptions creationOptions)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, state, creationOptions);
+ }
+
+ // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
+ // method can access the logic w/o declaring a TaskFactory<TResult> instance.
+ internal static Task<TResult> FromAsyncImpl(Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> endAction,
+ object state, TaskCreationOptions creationOptions)
+ {
+ if (beginMethod == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod);
+
+ if (endFunction == null && endAction == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod);
+
+ Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
+
+ TaskFactory.CheckFromAsyncOptions(creationOptions, true);
+
+ Task<TResult> promise = new Task<TResult>(state, creationOptions);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(promise);
+ }
+
+ try
+ {
+ // Do NOT change the code below.
+ // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly,
+ // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other
+ // change will likely brake 4.5 behavior so if possible never touch this code again.
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ //This is 4.5 behaviour
+ //if we don't require synchronization, a faster set result path is taken
+ var asyncResult = beginMethod(iar =>
+ {
+ if (!iar.CompletedSynchronously)
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ if (asyncResult.CompletedSynchronously)
+ {
+ Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed.");
+ FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false);
+ }
+ }
+ else
+ {
+ //This is the original 4.0 behaviour
+ var asyncResult = beginMethod(iar =>
+ {
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ }
+ }
+ catch
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.RemoveFromActiveTasks(promise.Id);
+ }
+
+ // Make sure we don't leave promise "dangling".
+ promise.TrySetResult(default(TResult));
+ throw;
+ }
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1>(
+ Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, object state)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1>(
+ Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, object state, TaskCreationOptions creationOptions)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, creationOptions);
+ }
+
+ // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
+ // method can access the logic w/o declaring a TaskFactory<TResult> instance.
+ internal static Task<TResult> FromAsyncImpl<TArg1>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> endAction,
+ TArg1 arg1, object state, TaskCreationOptions creationOptions)
+ {
+ if (beginMethod == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod);
+
+ if (endFunction == null && endAction == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endFunction);
+
+ Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
+
+ TaskFactory.CheckFromAsyncOptions(creationOptions, true);
+
+ Task<TResult> promise = new Task<TResult>(state, creationOptions);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(promise);
+ }
+
+ try
+ {
+ // Do NOT change the code below.
+ // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly,
+ // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other
+ // change will likely brake 4.5 behavior so if possible never touch this code again.
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ //if we don't require synchronization, a faster set result path is taken
+ var asyncResult = beginMethod(arg1, iar =>
+ {
+ if (!iar.CompletedSynchronously)
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ if (asyncResult.CompletedSynchronously)
+ {
+ Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed.");
+ FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false);
+ }
+ }
+ else
+ {
+ //quirk for previous versions
+ var asyncResult = beginMethod(arg1, iar =>
+ {
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ }
+ }
+ catch
+ {
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.RemoveFromActiveTasks(promise.Id);
+ }
+
+ // Make sure we don't leave promise "dangling".
+ promise.TrySetResult(default(TResult));
+ throw;
+ }
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2>(
+ Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, TArg2 arg2, object state)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2>(
+ Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, creationOptions);
+ }
+
+ // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
+ // method can access the logic w/o declaring a TaskFactory<TResult> instance.
+ internal static Task<TResult> FromAsyncImpl<TArg1, TArg2>(Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> endAction,
+ TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
+ {
+ if (beginMethod == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod);
+
+ if (endFunction == null && endAction == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod);
+
+ Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
+
+ TaskFactory.CheckFromAsyncOptions(creationOptions, true);
+
+ Task<TResult> promise = new Task<TResult>(state, creationOptions);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(promise);
+ }
+
+ try
+ {
+ // Do NOT change the code below.
+ // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly,
+ // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other
+ // change will likely brake 4.5 behavior so if possible never touch this code again.
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ //if we don't require synchronization, a faster set result path is taken
+ var asyncResult = beginMethod(arg1, arg2, iar =>
+ {
+ if (!iar.CompletedSynchronously)
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ if (asyncResult.CompletedSynchronously)
+ {
+ Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed.");
+ FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false);
+ }
+ }
+ else
+ {
+ //quirk for previous versions
+ var asyncResult = beginMethod(arg1, arg2, iar =>
+ {
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ }
+ }
+ catch
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.RemoveFromActiveTasks(promise.Id);
+ }
+
+ // Make sure we don't leave promise "dangling".
+ promise.TrySetResult(default(TResult));
+ throw;
+ }
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TArg3>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, TArg2 arg2, TArg3 arg3, object state)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TArg3>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod,
+ TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
+ {
+ return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, creationOptions);
+ }
+
+ // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
+ // method can access the logic w/o declaring a TaskFactory<TResult> instance.
+ internal static Task<TResult> FromAsyncImpl<TArg1, TArg2, TArg3>(Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> endAction,
+ TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
+ {
+ if (beginMethod == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod);
+
+ if (endFunction == null && endAction == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod);
+
+ Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
+
+ TaskFactory.CheckFromAsyncOptions(creationOptions, true);
+
+ Task<TResult> promise = new Task<TResult>(state, creationOptions);
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(promise);
+ }
+
+ try
+ {
+ // Do NOT change the code below.
+ // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly,
+ // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other
+ // change will likely brake 4.5 behavior so if possible never touch this code again.
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ //if we don't require synchronization, a faster set result path is taken
+ var asyncResult = beginMethod(arg1, arg2, arg3, iar =>
+ {
+ if (!iar.CompletedSynchronously)
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ if (asyncResult.CompletedSynchronously)
+ {
+ Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed.");
+ FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false);
+ }
+ }
+ else
+ {
+ //quirk for previous versions
+ var asyncResult = beginMethod(arg1, arg2, arg3, iar =>
+ {
+ FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true);
+ }, state);
+ }
+ }
+ catch
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.RemoveFromActiveTasks(promise.Id);
+ }
+
+ // Make sure we don't leave the promise "dangling".
+ promise.TrySetResult(default(TResult));
+ throw;
+ }
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Special internal-only FromAsync support used by System.IO to wrap
+ /// APM implementations with minimal overhead, avoiding unnecessary closure
+ /// and delegate allocations.
+ /// </summary>
+ /// <typeparam name="TInstance">Specifies the type of the instance on which the APM implementation lives.</typeparam>
+ /// <typeparam name="TArg1">Specifies the type containing the arguments.</typeparam>
+ /// <param name="thisRef">The instance from which the begin and end methods are invoked.</param>
+ /// <param name="beginMethod">The begin method.</param>
+ /// <param name="endMethod">The end method.</param>
+ /// <param name="args">The arguments.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ internal static Task<TResult> FromAsyncTrim<TInstance, TArgs>(
+ TInstance thisRef, TArgs args,
+ Func<TInstance, TArgs, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<TInstance, IAsyncResult, TResult> endMethod)
+ where TInstance : class
+ {
+ // Validate arguments, but only with asserts, as this is an internal only implementation.
+ Contract.Assert(thisRef != null, "Expected a non-null thisRef");
+ Contract.Assert(beginMethod != null, "Expected a non-null beginMethod");
+ Contract.Assert(endMethod != null, "Expected a non-null endMethod");
+
+ // Create the promise and start the operation.
+ // No try/catch is necessary here as we want exceptions to bubble out, and because
+ // the task doesn't have AttachedToParent set on it, there's no need to complete it in
+ // case of an exception occurring... we can just let it go unresolved.
+ var promise = new FromAsyncTrimPromise<TInstance>(thisRef, endMethod);
+ var asyncResult = beginMethod(thisRef, args, FromAsyncTrimPromise<TInstance>.s_completeFromAsyncResult, promise);
+
+ // If the IAsyncResult completed asynchronously, completing the promise will be handled by the callback.
+ // If it completed synchronously, we'll handle that here.
+ if (asyncResult.CompletedSynchronously)
+ {
+ Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed.");
+ promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization: false);
+ }
+
+ // Return the promise
+ return promise;
+ }
+
+ /// <summary>
+ /// A specialized task used by FromAsyncTrim. Stores relevant information as instance
+ /// state so that we can avoid unnecessary closure/delegate allocations.
+ /// </summary>
+ /// <typeparam name="TInstance">Specifies the type of the instance on which the APM implementation lives.</typeparam>
+ private sealed class FromAsyncTrimPromise<TInstance> : Task<TResult> where TInstance : class
+ {
+ /// <summary>A cached delegate used as the callback for the BeginXx method.</summary>
+ internal readonly static AsyncCallback s_completeFromAsyncResult = CompleteFromAsyncResult;
+
+ /// <summary>A reference to the object on which the begin/end methods are invoked.</summary>
+ private TInstance m_thisRef;
+ /// <summary>The end method.</summary>
+ private Func<TInstance, IAsyncResult, TResult> m_endMethod;
+
+ /// <summary>Initializes the promise.</summary>
+ /// <param name="thisRef">A reference to the object on which the begin/end methods are invoked.</param>
+ /// <param name="endMethod">The end method.</param>
+ internal FromAsyncTrimPromise(TInstance thisRef, Func<TInstance, IAsyncResult, TResult> endMethod) : base()
+ {
+ Contract.Requires(thisRef != null, "Expected a non-null thisRef");
+ Contract.Requires(endMethod != null, "Expected a non-null endMethod");
+ m_thisRef = thisRef;
+ m_endMethod = endMethod;
+ }
+
+ /// <summary>
+ /// Completes the asynchronous operation using information in the IAsyncResult.
+ /// IAsyncResult.AsyncState neeeds to be the FromAsyncTrimPromise to complete.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult for the async operation.</param>
+ internal static void CompleteFromAsyncResult(IAsyncResult asyncResult)
+ {
+ // Validate argument
+ if (asyncResult == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult);
+ Contract.EndContractBlock();
+
+ var promise = asyncResult.AsyncState as FromAsyncTrimPromise<TInstance>;
+ if (promise == null) ThrowHelper.ThrowArgumentException(ExceptionResource.InvalidOperation_WrongAsyncResultOrEndCalledMultiple, ExceptionArgument.asyncResult);
+
+ // Grab the relevant state and then null it out so that the task doesn't hold onto the state unnecessarily
+ var thisRef = promise.m_thisRef;
+ var endMethod = promise.m_endMethod;
+ promise.m_thisRef = default(TInstance);
+ promise.m_endMethod = null;
+ if (endMethod == null) ThrowHelper.ThrowArgumentException(ExceptionResource.InvalidOperation_WrongAsyncResultOrEndCalledMultiple, ExceptionArgument.asyncResult);
+
+ // Complete the promise. If the IAsyncResult completed synchronously,
+ // we'll instead complete the promise at the call site.
+ if (!asyncResult.CompletedSynchronously)
+ {
+ promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization:true);
+ }
+ }
+
+ /// <summary>Completes the promise.</summary>
+ /// <param name="requiresSynchronization">
+ /// true if synchronization is needed to completed the task;
+ /// false if the task may be completed without synchronization
+ /// because it hasn't been handed out.
+ /// </param>
+ /// <param name="thisRef">The target instance on which the end method should be called.</param>
+ /// <param name="endMethod">The end method to call to retrieve the result.</param>
+ /// <param name="asyncResult">The IAsyncResult for the async operation.</param>
+ /// <permission cref="requiresSynchronization">
+ /// Whether completing the task requires synchronization. This should be true
+ /// unless absolutely sure that the task has not yet been handed out to any consumers.
+ /// </permission>
+ internal void Complete(
+ TInstance thisRef, Func<TInstance, IAsyncResult, TResult> endMethod, IAsyncResult asyncResult,
+ bool requiresSynchronization)
+ {
+ Contract.Assert(!IsCompleted, "The task should not have been completed yet.");
+
+ // Run the end method and complete the task
+ bool successfullySet = false;
+ try
+ {
+ var result = endMethod(thisRef, asyncResult);
+ if (requiresSynchronization)
+ {
+ successfullySet = TrySetResult(result);
+ }
+ else
+ {
+ // If requiresSynchronization is false, we can use the DangerousSetResult
+ // method, which uses no synchronization to complete the task. This is
+ // only valid when the operation is completing synchronously such
+ // that the task has not yet been handed out to any consumers.
+ DangerousSetResult(result);
+ successfullySet = true;
+ }
+ }
+ catch (OperationCanceledException oce)
+ {
+ successfullySet = TrySetCanceled(oce.CancellationToken, oce);
+ }
+ catch (Exception exc)
+ {
+ successfullySet = TrySetException(exc);
+ }
+ Contract.Assert(successfullySet, "Expected the task to not yet be completed");
+ }
+ }
+
+ // Utility method to create a canceled future-style task.
+ // Used by ContinueWhenAll/Any to bail out early on a pre-canceled token.
+ private static Task<TResult> CreateCanceledTask(TaskContinuationOptions continuationOptions, CancellationToken ct)
+ {
+ TaskCreationOptions tco;
+ InternalTaskOptions dontcare;
+ Task.CreationOptionsFromContinuationOptions(continuationOptions, out tco, out dontcare);
+ return new Task<TResult>(true, default(TResult), tco, ct);
+ }
+
+ //
+ // ContinueWhenAll() methods
+ //
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+
+ // Core implementation of ContinueWhenAll -- the generic version
+ // Note: if you make any changes to this method, please do the same to the non-generic version too.
+ internal static Task<TResult> ContinueWhenAllImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks,
+ Func<Task<TAntecedentResult>[], TResult> continuationFunction, Action<Task<TAntecedentResult>[]> continuationAction,
+ TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ // check arguments
+ TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ //ArgumentNullException of continuationFunction or continuationAction is checked by the caller
+ Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null");
+ if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ Contract.EndContractBlock();
+
+ // Check tasks array and make defensive copy
+ Task<TAntecedentResult>[] tasksCopy = TaskFactory.CheckMultiContinuationTasksAndCopy<TAntecedentResult>(tasks);
+
+ // Bail early if cancellation has been requested.
+ if (cancellationToken.IsCancellationRequested
+ && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
+ )
+ {
+ return CreateCanceledTask(continuationOptions, cancellationToken);
+ }
+
+ // Call common ContinueWhenAll() setup logic, extract starter task.
+ var starter = TaskFactory.CommonCWAllLogic(tasksCopy);
+
+ // returned continuation task, off of starter
+ if (continuationFunction != null)
+ {
+ return starter.ContinueWith<TResult>(
+ // use a cached delegate
+ GenericDelegateCache<TAntecedentResult, TResult>.CWAllFuncDelegate,
+ continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ else
+ {
+ Contract.Assert(continuationAction != null);
+
+ return starter.ContinueWith<TResult>(
+ // use a cached delegate
+ GenericDelegateCache<TAntecedentResult, TResult>.CWAllActionDelegate,
+ continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ }
+
+ // Core implementation of ContinueWhenAll -- the non-generic version
+ // Note: if you make any changes to this method, please do the same to the generic version too.
+ internal static Task<TResult> ContinueWhenAllImpl(Task[] tasks,
+ Func<Task[], TResult> continuationFunction, Action<Task[]> continuationAction,
+ TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ // check arguments
+ TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ //ArgumentNullException of continuationFunction or continuationAction is checked by the caller
+ Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null");
+ if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ Contract.EndContractBlock();
+
+ // Check tasks array and make defensive copy
+ Task[] tasksCopy = TaskFactory.CheckMultiContinuationTasksAndCopy(tasks);
+
+ // Bail early if cancellation has been requested.
+ if (cancellationToken.IsCancellationRequested
+ && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
+ )
+ {
+ return CreateCanceledTask(continuationOptions, cancellationToken);
+ }
+
+ // Perform common ContinueWhenAll() setup logic, extract starter task
+ var starter = TaskFactory.CommonCWAllLogic(tasksCopy);
+
+ // returned continuation task, off of starter
+ if (continuationFunction != null)
+ {
+ return starter.ContinueWith(
+ //the following delegate avoids closure capture as much as possible
+ //completedTasks.Result == tasksCopy;
+ //state == continuationFunction
+ (completedTasks, state) =>
+ {
+ completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary();
+ return ((Func<Task[], TResult>)state)(completedTasks.Result);
+ },
+ continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ else
+ {
+ Contract.Assert(continuationAction != null);
+ return starter.ContinueWith<TResult>(
+ //the following delegate avoids closure capture as much as possible
+ //completedTasks.Result == tasksCopy;
+ //state == continuationAction
+ (completedTasks, state) =>
+ {
+ completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary();
+ ((Action<Task[]>)state)(completedTasks.Result); return default(TResult);
+ },
+ continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ }
+
+ //
+ // ContinueWhenAny() methods
+ //
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ // Core implementation of ContinueWhenAny, non-generic version
+ // Note: if you make any changes to this method, be sure to do the same to the generic version
+ internal static Task<TResult> ContinueWhenAnyImpl(Task[] tasks,
+ Func<Task, TResult> continuationFunction, Action<Task> continuationAction,
+ TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ // check arguments
+ TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ if(tasks.Length == 0) ThrowHelper.ThrowArgumentException( ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
+
+ //ArgumentNullException of continuationFunction or continuationAction is checked by the caller
+ Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null");
+ if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ Contract.EndContractBlock();
+
+ // Call common ContinueWhenAny() setup logic, extract starter
+ Task<Task> starter = TaskFactory.CommonCWAnyLogic(tasks);
+
+ // Bail early if cancellation has been requested.
+ if (cancellationToken.IsCancellationRequested
+ && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
+ )
+ {
+ return CreateCanceledTask(continuationOptions, cancellationToken);
+ }
+
+ // returned continuation task, off of starter
+ if (continuationFunction != null)
+ {
+ return starter.ContinueWith(
+ //the following delegate avoids closure capture as much as possible
+ //completedTask.Result is the winning task; state == continuationAction
+ (completedTask, state) => { return ((Func<Task, TResult>)state)(completedTask.Result); },
+ continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ else
+ {
+ Contract.Assert(continuationAction != null);
+ return starter.ContinueWith<TResult>(
+ //the following delegate avoids closure capture as much as possible
+ //completedTask.Result is the winning task; state == continuationAction
+ (completedTask, state) => { ((Action<Task>)state)(completedTask.Result); return default(TResult); },
+ continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ }
+
+
+ // Core implementation of ContinueWhenAny, generic version
+ // Note: if you make any changes to this method, be sure to do the same to the non-generic version
+ internal static Task<TResult> ContinueWhenAnyImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks,
+ Func<Task<TAntecedentResult>, TResult> continuationFunction, Action<Task<TAntecedentResult>> continuationAction,
+ TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ // check arguments
+ TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ if (tasks.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
+ //ArgumentNullException of continuationFunction or continuationAction is checked by the caller
+ Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null");
+ if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ Contract.EndContractBlock();
+
+ // Call common ContinueWhenAny setup logic, extract starter
+ var starter = TaskFactory.CommonCWAnyLogic(tasks);
+
+ // Bail early if cancellation has been requested.
+ if (cancellationToken.IsCancellationRequested
+ && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
+ )
+ {
+ return CreateCanceledTask(continuationOptions, cancellationToken);
+ }
+
+ // returned continuation task, off of starter
+ if (continuationFunction != null)
+ {
+ return starter.ContinueWith<TResult>(
+ // Use a cached delegate
+ GenericDelegateCache<TAntecedentResult, TResult>.CWAnyFuncDelegate,
+ continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ else
+ {
+ Contract.Assert(continuationAction != null);
+ return starter.ContinueWith<TResult>(
+ // Use a cached delegate
+ GenericDelegateCache<TAntecedentResult,TResult>.CWAnyActionDelegate,
+ continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+ }
+ }
+
+ // For the ContinueWhenAnyImpl/ContinueWhenAllImpl methods that are generic on TAntecedentResult,
+ // the compiler won't cache the internal ContinueWith delegate because it is generic on both
+ // TAntecedentResult and TResult. The GenericDelegateCache serves as a cache for those delegates.
+ internal static class GenericDelegateCache<TAntecedentResult, TResult>
+ {
+ // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(non-null continuationFunction)
+ internal static Func<Task<Task>, object, TResult> CWAnyFuncDelegate =
+ (Task<Task> wrappedWinner, object state) =>
+ {
+ var func = (Func<Task<TAntecedentResult>, TResult>)state;
+ var arg = (Task<TAntecedentResult>)wrappedWinner.Result;
+ return func(arg);
+ };
+
+ // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(non-null continuationAction)
+ internal static Func<Task<Task>, object, TResult> CWAnyActionDelegate =
+ (Task<Task> wrappedWinner, object state) =>
+ {
+ var action = (Action<Task<TAntecedentResult>>)state;
+ var arg = (Task<TAntecedentResult>)wrappedWinner.Result;
+ action(arg);
+ return default(TResult);
+ };
+
+ // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(non-null continuationFunction)
+ internal static Func<Task<Task<TAntecedentResult>[]>, object, TResult> CWAllFuncDelegate =
+ (Task<Task<TAntecedentResult>[]> wrappedAntecedents, object state) =>
+ {
+ wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary();
+ var func = (Func<Task<TAntecedentResult>[], TResult>)state;
+ return func(wrappedAntecedents.Result);
+ };
+
+ // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(non-null continuationAction)
+ internal static Func<Task<Task<TAntecedentResult>[]>, object, TResult> CWAllActionDelegate =
+ (Task<Task<TAntecedentResult>[]> wrappedAntecedents, object state) =>
+ {
+ wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary();
+ var action = (Action<Task<TAntecedentResult>[]>)state;
+ action(wrappedAntecedents.Result);
+ return default(TResult);
+ };
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs
new file mode 100644
index 0000000000..b8155d017e
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+// Windows.Foundation.Diagnostics cannot be referenced from managed code because
+// they're hidden by the metadata adapter. We redeclare the interfaces manually
+// to be able to talk to native WinRT objects.
+namespace Windows.Foundation.Diagnostics
+{
+ [ComImport]
+ [Guid("50850B26-267E-451B-A890-AB6A370245EE")]
+ [WindowsRuntimeImport]
+ internal interface IAsyncCausalityTracerStatics
+ {
+ void TraceOperationCreation(CausalityTraceLevel traceLevel, CausalitySource source, Guid platformId, ulong operationId, string operationName, ulong relatedContext);
+ void TraceOperationCompletion(CausalityTraceLevel traceLevel, CausalitySource source, Guid platformId, ulong operationId, AsyncCausalityStatus status);
+ void TraceOperationRelation(CausalityTraceLevel traceLevel, CausalitySource source, Guid platformId, ulong operationId, CausalityRelation relation);
+ void TraceSynchronousWorkStart(CausalityTraceLevel traceLevel, CausalitySource source, Guid platformId, ulong operationId, CausalitySynchronousWork work);
+ void TraceSynchronousWorkCompletion(CausalityTraceLevel traceLevel, CausalitySource source, CausalitySynchronousWork work);
+ //These next 2 functions could've been represented as an event except that the EventRegistrationToken wasn't being propagated to WinRT
+ EventRegistrationToken add_TracingStatusChanged(System.EventHandler<TracingStatusChangedEventArgs> eventHandler);
+ void remove_TracingStatusChanged(EventRegistrationToken token);
+ }
+
+ [ComImport]
+ [Guid("410B7711-FF3B-477F-9C9A-D2EFDA302DC3")]
+ [WindowsRuntimeImport]
+ internal interface ITracingStatusChangedEventArgs
+ {
+ bool Enabled { get; }
+ CausalityTraceLevel TraceLevel { get; }
+ }
+
+ // We need this dummy class to satisfy a QI when the TracingStatusChangedHandler
+ // after being stored in a GIT cookie and then called by the WinRT API. This usually
+ // happens when calling a MAnaged WinMD which access this feature.
+ [ComImport]
+ [Guid("410B7711-FF3B-477F-9C9A-D2EFDA302DC3")]
+ [WindowsRuntimeImport]
+ internal sealed class TracingStatusChangedEventArgs : ITracingStatusChangedEventArgs
+ {
+ public extern bool Enabled
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ get;
+ }
+
+ public extern CausalityTraceLevel TraceLevel
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ get;
+ }
+ }
+
+ internal enum CausalityRelation
+ {
+ AssignDelegate,
+ Join,
+ Choice,
+ Cancel,
+ Error
+ }
+
+ internal enum CausalitySource
+ {
+ Application,
+ Library,
+ System
+ }
+
+ internal enum CausalitySynchronousWork
+ {
+ CompletionNotification,
+ ProgressNotification,
+ Execution
+ }
+
+ internal enum CausalityTraceLevel
+ {
+ Required,
+ Important,
+ Verbose
+ }
+
+ internal enum AsyncCausalityStatus
+ {
+ Canceled = 2,
+ Completed = 1,
+ Error = 3,
+ Started = 0
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/Parallel.cs b/src/mscorlib/src/System/Threading/Tasks/Parallel.cs
new file mode 100644
index 0000000000..5ec2ae33c0
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/Parallel.cs
@@ -0,0 +1,3594 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A helper class that contains parallel versions of various looping constructs. This
+// internally uses the task parallel library, but takes care to expose very little
+// evidence of this infrastructure being used.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Security.Permissions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Diagnostics.Contracts;
+
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Stores options that configure the operation of methods on the
+ /// <see cref="T:System.Threading.Tasks.Parallel">Parallel</see> class.
+ /// </summary>
+ /// <remarks>
+ /// By default, methods on the Parallel class attempt to utilize all available processors, are non-cancelable, and target
+ /// the default TaskScheduler (TaskScheduler.Default). <see cref="ParallelOptions"/> enables
+ /// overriding these defaults.
+ /// </remarks>
+ public class ParallelOptions
+ {
+ private TaskScheduler m_scheduler;
+ private int m_maxDegreeOfParallelism;
+ private CancellationToken m_cancellationToken;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ParallelOptions"/> class.
+ /// </summary>
+ /// <remarks>
+ /// This constructor initializes the instance with default values. <see cref="MaxDegreeOfParallelism"/>
+ /// is initialized to -1, signifying that there is no upper bound set on how much parallelism should
+ /// be employed. <see cref="CancellationToken"/> is initialized to a non-cancelable token,
+ /// and <see cref="TaskScheduler"/> is initialized to the default scheduler (TaskScheduler.Default).
+ /// All of these defaults may be overwritten using the property set accessors on the instance.
+ /// </remarks>
+ public ParallelOptions()
+ {
+ m_scheduler = TaskScheduler.Default;
+ m_maxDegreeOfParallelism = -1;
+ m_cancellationToken = CancellationToken.None;
+ }
+
+ /// <summary>
+ /// Gets or sets the <see cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// associated with this <see cref="ParallelOptions"/> instance. Setting this property to null
+ /// indicates that the current scheduler should be used.
+ /// </summary>
+ public TaskScheduler TaskScheduler
+ {
+ get { return m_scheduler; }
+ set { m_scheduler = value; }
+ }
+
+ // Convenience property used by TPL logic
+ internal TaskScheduler EffectiveTaskScheduler
+ {
+ get
+ {
+ if (m_scheduler == null) return TaskScheduler.Current;
+ else return m_scheduler;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum degree of parallelism enabled by this ParallelOptions instance.
+ /// </summary>
+ /// <remarks>
+ /// The <see cref="MaxDegreeOfParallelism"/> limits the number of concurrent operations run by <see
+ /// cref="T:System.Threading.Tasks.Parallel">Parallel</see> method calls that are passed this
+ /// ParallelOptions instance to the set value, if it is positive. If <see
+ /// cref="MaxDegreeOfParallelism"/> is -1, then there is no limit placed on the number of concurrently
+ /// running operations.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when this <see cref="MaxDegreeOfParallelism"/> is set to 0 or some
+ /// value less than -1.
+ /// </exception>
+ public int MaxDegreeOfParallelism
+ {
+ get { return m_maxDegreeOfParallelism; }
+ set
+ {
+ if ((value == 0) || (value < -1))
+ throw new ArgumentOutOfRangeException("MaxDegreeOfParallelism");
+ m_maxDegreeOfParallelism = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
+ /// associated with this <see cref="ParallelOptions"/> instance.
+ /// </summary>
+ /// <remarks>
+ /// Providing a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
+ /// to a <see cref="T:System.Threading.Tasks.Parallel">Parallel</see> method enables the operation to be
+ /// exited early. Code external to the operation may cancel the token, and if the operation observes the
+ /// token being set, it may exit early by throwing an
+ /// <see cref="T:System.OperationCanceledException"/>.
+ /// </remarks>
+ public CancellationToken CancellationToken
+ {
+ get { return m_cancellationToken; }
+ set { m_cancellationToken = value; }
+ }
+
+ internal int EffectiveMaxConcurrencyLevel
+ {
+ get
+ {
+ int rval = MaxDegreeOfParallelism;
+ int schedulerMax = EffectiveTaskScheduler.MaximumConcurrencyLevel;
+ if ((schedulerMax > 0) && (schedulerMax != Int32.MaxValue))
+ {
+ rval = (rval == -1) ? schedulerMax : Math.Min(schedulerMax, rval);
+ }
+ return rval;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Provides support for parallel loops and regions.
+ /// </summary>
+ /// <remarks>
+ /// The <see cref="T:System.Threading.Tasks.Parallel"/> class provides library-based data parallel replacements
+ /// for common operations such as for loops, for each loops, and execution of a set of statements.
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public static class Parallel
+ {
+ // static counter for generating unique Fork/Join Context IDs to be used in ETW events
+ internal static int s_forkJoinContextID;
+
+ // We use a stride for loops to amortize the frequency of interlocked operations.
+ internal const int DEFAULT_LOOP_STRIDE = 16;
+
+ // Static variable to hold default parallel options
+ internal static ParallelOptions s_defaultParallelOptions = new ParallelOptions();
+
+ /// <summary>
+ /// Executes each of the provided actions, possibly in parallel.
+ /// </summary>
+ /// <param name="actions">An array of <see cref="T:System.Action">Actions</see> to execute.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="actions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="actions"/> array contains a null element.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown when any
+ /// action in the <paramref name="actions"/> array throws an exception.</exception>
+ /// <remarks>
+ /// This method can be used to execute a set of operations, potentially in parallel.
+ /// No guarantees are made about the order in which the operations execute or whether
+ /// they execute in parallel. This method does not return until each of the
+ /// provided operations has completed, regardless of whether completion
+ /// occurs due to normal or exceptional termination.
+ /// </remarks>
+ public static void Invoke(params Action[] actions)
+ {
+ Invoke(s_defaultParallelOptions, actions);
+ }
+
+ /// <summary>
+ /// Executes each of the provided actions, possibly in parallel.
+ /// </summary>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="actions">An array of <see cref="T:System.Action">Actions</see> to execute.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="actions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="actions"/> array contains a null element.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown when any
+ /// action in the <paramref name="actions"/> array throws an exception.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <remarks>
+ /// This method can be used to execute a set of operations, potentially in parallel.
+ /// No guarantees are made about the order in which the operations execute or whether
+ /// the they execute in parallel. This method does not return until each of the
+ /// provided operations has completed, regardless of whether completion
+ /// occurs due to normal or exceptional termination.
+ /// </remarks>
+ public static void Invoke(ParallelOptions parallelOptions, params Action[] actions)
+ {
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ // Throw an ODE if we're passed a disposed CancellationToken.
+ if (parallelOptions.CancellationToken.CanBeCanceled
+ && AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ {
+ parallelOptions.CancellationToken.ThrowIfSourceDisposed();
+ }
+ // Quit early if we're already canceled -- avoid a bunch of work.
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+
+ // We must validate that the actions array contains no null elements, and also
+ // make a defensive copy of the actions array.
+ Action[] actionsCopy = new Action[actions.Length];
+ for (int i = 0; i < actionsCopy.Length; i++)
+ {
+ actionsCopy[i] = actions[i];
+ if (actionsCopy[i] == null)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Parallel_Invoke_ActionNull"));
+ }
+ }
+
+ // ETW event for Parallel Invoke Begin
+ int forkJoinContextID = 0;
+ Task callerTask = null;
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ forkJoinContextID = Interlocked.Increment(ref s_forkJoinContextID);
+ callerTask = Task.InternalCurrent;
+ TplEtwProvider.Log.ParallelInvokeBegin((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID, TplEtwProvider.ForkJoinOperationType.ParallelInvoke,
+ actionsCopy.Length);
+ }
+
+#if DEBUG
+ actions = null; // Ensure we don't accidentally use this below.
+#endif
+
+ // If we have no work to do, we are done.
+ if (actionsCopy.Length < 1) return;
+
+ // In the algorithm below, if the number of actions is greater than this, we automatically
+ // use Parallel.For() to handle the actions, rather than the Task-per-Action strategy.
+ const int SMALL_ACTIONCOUNT_LIMIT = 10;
+
+ try
+ {
+ // If we've gotten this far, it's time to process the actions.
+ if ((actionsCopy.Length > SMALL_ACTIONCOUNT_LIMIT) ||
+ (parallelOptions.MaxDegreeOfParallelism != -1 && parallelOptions.MaxDegreeOfParallelism < actionsCopy.Length))
+ {
+ // Used to hold any exceptions encountered during action processing
+ ConcurrentQueue<Exception> exceptionQ = null; // will be lazily initialized if necessary
+
+ // This is more efficient for a large number of actions, or for enforcing MaxDegreeOfParallelism.
+ try
+ {
+ // Launch a self-replicating task to handle the execution of all actions.
+ // The use of a self-replicating task allows us to use as many cores
+ // as are available, and no more. The exception to this rule is
+ // that, in the case of a blocked action, the ThreadPool may inject
+ // extra threads, which means extra tasks can run.
+ int actionIndex = 0;
+ ParallelForReplicatingTask rootTask = new ParallelForReplicatingTask(parallelOptions, delegate
+ {
+ // Each for-task will pull an action at a time from the list
+ int myIndex = Interlocked.Increment(ref actionIndex); // = index to use + 1
+ while (myIndex <= actionsCopy.Length)
+ {
+ // Catch and store any exceptions. If we don't catch them, the self-replicating
+ // task will exit, and that may cause other SR-tasks to exit.
+ // And (absent cancellation) we want all actions to execute.
+ try
+ {
+ actionsCopy[myIndex - 1]();
+ }
+ catch (Exception e)
+ {
+ LazyInitializer.EnsureInitialized<ConcurrentQueue<Exception>>(ref exceptionQ, () => { return new ConcurrentQueue<Exception>(); });
+ exceptionQ.Enqueue(e);
+ }
+
+ // Check for cancellation. If it is encountered, then exit the delegate.
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+
+ // You're still in the game. Grab your next action index.
+ myIndex = Interlocked.Increment(ref actionIndex);
+ }
+ }, TaskCreationOptions.None, InternalTaskOptions.SelfReplicating);
+
+ rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler);
+ rootTask.Wait();
+ }
+ catch (Exception e)
+ {
+ LazyInitializer.EnsureInitialized<ConcurrentQueue<Exception>>(ref exceptionQ, () => { return new ConcurrentQueue<Exception>(); });
+
+ // Since we're consuming all action exceptions, there are very few reasons that
+ // we would see an exception here. Two that come to mind:
+ // (1) An OCE thrown by one or more actions (AggregateException thrown)
+ // (2) An exception thrown from the ParallelForReplicatingTask constructor
+ // (regular exception thrown).
+ // We'll need to cover them both.
+ AggregateException ae = e as AggregateException;
+ if (ae != null)
+ {
+ // Strip off outer container of an AggregateException, because downstream
+ // logic needs OCEs to be at the top level.
+ foreach (Exception exc in ae.InnerExceptions) exceptionQ.Enqueue(exc);
+ }
+ else
+ {
+ exceptionQ.Enqueue(e);
+ }
+ }
+
+ // If we have encountered any exceptions, then throw.
+ if ((exceptionQ != null) && (exceptionQ.Count > 0))
+ {
+ ThrowIfReducableToSingleOCE(exceptionQ, parallelOptions.CancellationToken);
+ throw new AggregateException(exceptionQ);
+ }
+ }
+ else
+ {
+ // This is more efficient for a small number of actions and no DOP support
+
+ // Initialize our array of tasks, one per action.
+ Task[] tasks = new Task[actionsCopy.Length];
+
+ // One more check before we begin...
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+
+ // Launch all actions as tasks
+ for (int i = 1; i < tasks.Length; i++)
+ {
+ tasks[i] = Task.Factory.StartNew(actionsCopy[i], parallelOptions.CancellationToken, TaskCreationOptions.None,
+ InternalTaskOptions.None, parallelOptions.EffectiveTaskScheduler);
+ }
+
+ // Optimization: Use current thread to run something before we block waiting for all tasks.
+ tasks[0] = new Task(actionsCopy[0]);
+ tasks[0].RunSynchronously(parallelOptions.EffectiveTaskScheduler);
+
+ // Now wait for the tasks to complete. This will not unblock until all of
+ // them complete, and it will throw an exception if one or more of them also
+ // threw an exception. We let such exceptions go completely unhandled.
+ try
+ {
+ if (tasks.Length <= 4)
+ {
+ // for 4 or less tasks, the sequential waitall version is faster
+ Task.FastWaitAll(tasks);
+ }
+ else
+ {
+ // otherwise we revert to the regular WaitAll which delegates the multiple wait to the cooperative event.
+ Task.WaitAll(tasks);
+ }
+ }
+ catch (AggregateException aggExp)
+ {
+ // see if we can combine it into a single OCE. If not propagate the original exception
+ ThrowIfReducableToSingleOCE(aggExp.InnerExceptions, parallelOptions.CancellationToken);
+ throw;
+ }
+ finally
+ {
+ for (int i = 0; i < tasks.Length; i++)
+ {
+ if (tasks[i].IsCompleted) tasks[i].Dispose();
+ }
+ }
+ }
+ }
+ finally
+ {
+ // ETW event for Parallel Invoke End
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelInvokeEnd((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the iteration count (an Int32) as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForWorker<object>(
+ fromInclusive, toExclusive,
+ s_defaultParallelOptions,
+ body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the iteration count (an Int64) as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult For(long fromInclusive, long toExclusive, Action<long> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForWorker64<object>(
+ fromInclusive, toExclusive, s_defaultParallelOptions,
+ body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the iteration count (an Int32) as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult For(int fromInclusive, int toExclusive, ParallelOptions parallelOptions, Action<int> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForWorker<object>(
+ fromInclusive, toExclusive, parallelOptions,
+ body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the iteration count (an Int64) as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult For(long fromInclusive, long toExclusive, ParallelOptions parallelOptions, Action<long> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForWorker64<object>(
+ fromInclusive, toExclusive, parallelOptions,
+ body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int32),
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </para>
+ /// <para>
+ /// Calling <see cref="System.Threading.Tasks.ParallelLoopState.Break()">ParallelLoopState.Break()</see>
+ /// informs the For operation that iterations after the current one need not
+ /// execute. However, all iterations before the current one will still need to be executed if they have not already.
+ /// Therefore, calling Break is similar to using a break operation within a
+ /// conventional for loop in a language like C#, but it is not a perfect substitute: for example, there is no guarantee that iterations
+ /// after the current one will definitely not execute.
+ /// </para>
+ /// <para>
+ /// If executing all iterations before the current one is not necessary,
+ /// <see cref="System.Threading.Tasks.ParallelLoopState.Stop()">ParallelLoopState.Stop()</see>
+ /// should be preferred to using Break. Calling Stop informs the For loop that it may abandon all remaining
+ /// iterations, regardless of whether they're for interations above or below the current,
+ /// since all required work has already been completed. As with Break, however, there are no guarantees regarding
+ /// which other iterations will not execute.
+ /// </para>
+ /// <para>
+ /// When a loop is ended prematurely, the <see cref="T:ParallelLoopState"/> that's returned will contain
+ /// relevant information about the loop's completion.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int, ParallelLoopState> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForWorker<object>(
+ fromInclusive, toExclusive, s_defaultParallelOptions,
+ null, body, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int64),
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </remarks>
+ public static ParallelLoopResult For(long fromInclusive, long toExclusive, Action<long, ParallelLoopState> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForWorker64<object>(
+ fromInclusive, toExclusive, s_defaultParallelOptions,
+ null, body, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int32),
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </remarks>
+ public static ParallelLoopResult For(int fromInclusive, int toExclusive, ParallelOptions parallelOptions, Action<int, ParallelLoopState> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForWorker<object>(
+ fromInclusive, toExclusive, parallelOptions,
+ null, body, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int64),
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </remarks>
+ public static ParallelLoopResult For(long fromInclusive, long toExclusive, ParallelOptions parallelOptions,
+ Action<long, ParallelLoopState> body)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForWorker64<object>(
+ fromInclusive, toExclusive, parallelOptions,
+ null, body, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int32),
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult For<TLocal>(
+ int fromInclusive, int toExclusive,
+ Func<TLocal> localInit,
+ Func<int, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ return ForWorker(
+ fromInclusive, toExclusive, s_defaultParallelOptions,
+ null, null, body, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel. Supports 64-bit indices.
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int64),
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult For<TLocal>(
+ long fromInclusive, long toExclusive,
+ Func<TLocal> localInit,
+ Func<long, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ return ForWorker64(
+ fromInclusive, toExclusive, s_defaultParallelOptions,
+ null, null, body, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int32),
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult For<TLocal>(
+ int fromInclusive, int toExclusive, ParallelOptions parallelOptions,
+ Func<TLocal> localInit,
+ Func<int, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForWorker(
+ fromInclusive, toExclusive, parallelOptions,
+ null, null, body, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for loop in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="fromInclusive">The start index, inclusive.</param>
+ /// <param name="toExclusive">The end index, exclusive.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each value in the iteration range:
+ /// [fromInclusive, toExclusive). It is provided with the following parameters: the iteration count (an Int64),
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult For<TLocal>(
+ long fromInclusive, long toExclusive, ParallelOptions parallelOptions,
+ Func<TLocal> localInit,
+ Func<long, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+
+ return ForWorker64(
+ fromInclusive, toExclusive, parallelOptions,
+ null, null, body, localInit, localFinally);
+ }
+
+
+
+
+
+
+
+ /// <summary>
+ /// Performs the major work of the parallel for loop. It assumes that argument validation has already
+ /// been performed by the caller. This function's whole purpose in life is to enable as much reuse of
+ /// common implementation details for the various For overloads we offer. Without it, we'd end up
+ /// with lots of duplicate code. It handles: (1) simple for loops, (2) for loops that depend on
+ /// ParallelState, and (3) for loops with thread local data.
+ ///
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the local data.</typeparam>
+ /// <param name="fromInclusive">The loop's start index, inclusive.</param>
+ /// <param name="toExclusive">The loop's end index, exclusive.</param>
+ /// <param name="parallelOptions">A ParallelOptions instance.</param>
+ /// <param name="body">The simple loop body.</param>
+ /// <param name="bodyWithState">The loop body for ParallelState overloads.</param>
+ /// <param name="bodyWithLocal">The loop body for thread local state overloads.</param>
+ /// <param name="localInit">A selector function that returns new thread local state.</param>
+ /// <param name="localFinally">A cleanup function to destroy thread local state.</param>
+ /// <remarks>Only one of the body arguments may be supplied (i.e. they are exclusive).</remarks>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult"/> structure.</returns>
+ private static ParallelLoopResult ForWorker<TLocal>(
+ int fromInclusive, int toExclusive,
+ ParallelOptions parallelOptions,
+ Action<int> body,
+ Action<int, ParallelLoopState> bodyWithState,
+ Func<int, ParallelLoopState, TLocal, TLocal> bodyWithLocal,
+ Func<TLocal> localInit, Action<TLocal> localFinally)
+ {
+ Contract.Assert(((body == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) + (bodyWithLocal == null ? 0 : 1)) == 1,
+ "expected exactly one body function to be supplied");
+ Contract.Assert(bodyWithLocal != null || (localInit == null && localFinally == null),
+ "thread local functions should only be supplied for loops w/ thread local bodies");
+
+ // Instantiate our result. Specifics will be filled in later.
+ ParallelLoopResult result = new ParallelLoopResult();
+
+ // We just return immediately if 'to' is smaller (or equal to) 'from'.
+ if (toExclusive <= fromInclusive)
+ {
+ result.m_completed = true;
+ return result;
+ }
+
+ // For all loops we need a shared flag even though we don't have a body with state,
+ // because the shared flag contains the exceptional bool, which triggers other workers
+ // to exit their loops if one worker catches an exception
+ ParallelLoopStateFlags32 sharedPStateFlags = new ParallelLoopStateFlags32();
+
+ TaskCreationOptions creationOptions = TaskCreationOptions.None;
+ InternalTaskOptions internalOptions = InternalTaskOptions.SelfReplicating;
+
+ // Before getting started, do a quick peek to see if we have been canceled already
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ {
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+ }
+
+ // initialize ranges with passed in loop arguments and expected number of workers
+ int numExpectedWorkers = (parallelOptions.EffectiveMaxConcurrencyLevel == -1) ?
+ PlatformHelper.ProcessorCount :
+ parallelOptions.EffectiveMaxConcurrencyLevel;
+ RangeManager rangeManager = new RangeManager(fromInclusive, toExclusive, 1, numExpectedWorkers);
+
+ // Keep track of any cancellations
+ OperationCanceledException oce = null;
+
+ CancellationTokenRegistration ctr = new CancellationTokenRegistration();
+
+ // if cancellation is enabled, we need to register a callback to stop the loop when it gets signaled
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr = parallelOptions.CancellationToken.InternalRegisterWithoutEC((o) =>
+ {
+ // Cause processing to stop
+ sharedPStateFlags.Cancel();
+ // Record our cancellation
+ oce = new OperationCanceledException(parallelOptions.CancellationToken);
+ }, null);
+ }
+
+ // ETW event for Parallel For begin
+ int forkJoinContextID = 0;
+ Task callingTask = null;
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ forkJoinContextID = Interlocked.Increment(ref s_forkJoinContextID);
+ callingTask = Task.InternalCurrent;
+ TplEtwProvider.Log.ParallelLoopBegin((callingTask != null ? callingTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callingTask != null ? callingTask.Id : 0),
+ forkJoinContextID, TplEtwProvider.ForkJoinOperationType.ParallelFor,
+ fromInclusive, toExclusive);
+ }
+
+ ParallelForReplicatingTask rootTask = null;
+
+ try
+ {
+ // this needs to be in try-block because it can throw in BuggyScheduler.MaxConcurrencyLevel
+ rootTask = new ParallelForReplicatingTask(
+ parallelOptions,
+ delegate
+ {
+ //
+ // first thing we do upon enterying the task is to register as a new "RangeWorker" with the
+ // shared RangeManager instance.
+ //
+ // If this call returns a RangeWorker struct which wraps the state needed by this task
+ //
+ // We need to call FindNewWork32() on it to see whether there's a chunk available.
+ //
+
+
+ // Cache some information about the current task
+ Task currentWorkerTask = Task.InternalCurrent;
+ bool bIsRootTask = (currentWorkerTask == rootTask);
+
+ RangeWorker currentWorker = new RangeWorker();
+ Object savedStateFromPreviousReplica = currentWorkerTask.SavedStateFromPreviousReplica;
+
+ if (savedStateFromPreviousReplica is RangeWorker)
+ currentWorker = (RangeWorker)savedStateFromPreviousReplica;
+ else
+ currentWorker = rangeManager.RegisterNewWorker();
+
+
+
+ // These are the local index values to be used in the sequential loop.
+ // Their values filled in by FindNewWork32
+ int nFromInclusiveLocal;
+ int nToExclusiveLocal;
+
+ if (currentWorker.FindNewWork32(out nFromInclusiveLocal, out nToExclusiveLocal) == false ||
+ sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal) == true)
+ {
+ return; // no need to run
+ }
+
+ // ETW event for ParallelFor Worker Fork
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelFork((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+
+ TLocal localValue = default(TLocal);
+ bool bLocalValueInitialized = false; // Tracks whether localInit ran without exceptions, so that we can skip localFinally if it wasn't
+
+ try
+ {
+ // Create a new state object that references the shared "stopped" and "exceptional" flags
+ // If needed, it will contain a new instance of thread-local state by invoking the selector.
+ ParallelLoopState32 state = null;
+
+ if (bodyWithState != null)
+ {
+ Contract.Assert(sharedPStateFlags != null);
+ state = new ParallelLoopState32(sharedPStateFlags);
+ }
+ else if (bodyWithLocal != null)
+ {
+ Contract.Assert(sharedPStateFlags != null);
+ state = new ParallelLoopState32(sharedPStateFlags);
+ if (localInit != null)
+ {
+ localValue = localInit();
+ bLocalValueInitialized = true;
+ }
+ }
+
+ // initialize a loop timer which will help us decide whether we should exit early
+ LoopTimer loopTimer = new LoopTimer(rootTask.ActiveChildCount);
+
+ // Now perform the loop itself.
+ do
+ {
+ if (body != null)
+ {
+ for (int j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop()); // the no-arg version is used since we have no state
+ j += 1)
+ {
+
+ body(j);
+ }
+ }
+ else if (bodyWithState != null)
+ {
+ for (int j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop(j));
+ j += 1)
+ {
+
+ state.CurrentIteration = j;
+ bodyWithState(j, state);
+ }
+ }
+ else
+ {
+ for (int j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop(j));
+ j += 1)
+ {
+ state.CurrentIteration = j;
+ localValue = bodyWithLocal(j, state, localValue);
+ }
+ }
+
+ // Cooperative multitasking workaround for AppDomain fairness.
+ // Check if allowed loop time is exceeded, if so save current state and return. The self replicating task logic
+ // will detect this, and queue up a replacement task. Note that we don't do this on the root task.
+ if (!bIsRootTask && loopTimer.LimitExceeded())
+ {
+ currentWorkerTask.SavedStateForNextReplica = (object)currentWorker;
+ break;
+ }
+
+ }
+ // Exit if we can't find new work, or if the loop was stoppped.
+ while (currentWorker.FindNewWork32(out nFromInclusiveLocal, out nToExclusiveLocal) &&
+ ((sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE) ||
+ !sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal)));
+ }
+ catch
+ {
+ // if we catch an exception in a worker, we signal the other workers to exit the loop, and we rethrow
+ sharedPStateFlags.SetExceptional();
+ throw;
+ }
+ finally
+ {
+ // If a cleanup function was specified, call it. Otherwise, if the type is
+ // IDisposable, we will invoke Dispose on behalf of the user.
+ if (localFinally != null && bLocalValueInitialized)
+ {
+ localFinally(localValue);
+ }
+
+ // ETW event for ParallelFor Worker Join
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelJoin((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+ }
+ },
+ creationOptions, internalOptions);
+
+ rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler); // might throw TSE
+ rootTask.Wait();
+
+ // If we made a cancellation registration, we need to clean it up now before observing the OCE
+ // Otherwise we could be caught in the middle of a callback, and observe PLS_STOPPED, but oce = null
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // If we got through that with no exceptions, and we were canceled, then
+ // throw our cancellation exception
+ if (oce != null) throw oce;
+ }
+ catch (AggregateException aggExp)
+ {
+ // if we made a cancellation registration, and rootTask.Wait threw, we need to clean it up here
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // see if we can combine it into a single OCE. If not propagate the original exception
+ ThrowIfReducableToSingleOCE(aggExp.InnerExceptions, parallelOptions.CancellationToken);
+ throw;
+ }
+ catch (TaskSchedulerException)
+ {
+ // if we made a cancellation registration, and rootTask.RunSynchronously threw, we need to clean it up here
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+ throw;
+ }
+ finally
+ {
+ int sb_status = sharedPStateFlags.LoopStateFlags;
+ result.m_completed = (sb_status == ParallelLoopStateFlags.PLS_NONE);
+ if ((sb_status & ParallelLoopStateFlags.PLS_BROKEN) != 0)
+ {
+ result.m_lowestBreakIteration = sharedPStateFlags.LowestBreakIteration;
+ }
+
+ if ((rootTask != null) && rootTask.IsCompleted) rootTask.Dispose();
+
+ // ETW event for Parallel For End
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ int nTotalIterations = 0;
+
+ // calculate how many iterations we ran in total
+ if (sb_status == ParallelLoopStateFlags.PLS_NONE)
+ nTotalIterations = toExclusive - fromInclusive;
+ else if ((sb_status & ParallelLoopStateFlags.PLS_BROKEN) != 0)
+ nTotalIterations = sharedPStateFlags.LowestBreakIteration - fromInclusive;
+ else
+ nTotalIterations = -1; //PLS_STOPPED! We can't determine this if we were stopped..
+
+ TplEtwProvider.Log.ParallelLoopEnd((callingTask != null ? callingTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callingTask != null ? callingTask.Id : 0),
+ forkJoinContextID, nTotalIterations);
+ }
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Performs the major work of the 64-bit parallel for loop. It assumes that argument validation has already
+ /// been performed by the caller. This function's whole purpose in life is to enable as much reuse of
+ /// common implementation details for the various For overloads we offer. Without it, we'd end up
+ /// with lots of duplicate code. It handles: (1) simple for loops, (2) for loops that depend on
+ /// ParallelState, and (3) for loops with thread local data.
+ ///
+ /// </summary>
+ /// <typeparam name="TLocal">The type of the local data.</typeparam>
+ /// <param name="fromInclusive">The loop's start index, inclusive.</param>
+ /// <param name="toExclusive">The loop's end index, exclusive.</param>
+ /// <param name="parallelOptions">A ParallelOptions instance.</param>
+ /// <param name="body">The simple loop body.</param>
+ /// <param name="bodyWithState">The loop body for ParallelState overloads.</param>
+ /// <param name="bodyWithLocal">The loop body for thread local state overloads.</param>
+ /// <param name="localInit">A selector function that returns new thread local state.</param>
+ /// <param name="localFinally">A cleanup function to destroy thread local state.</param>
+ /// <remarks>Only one of the body arguments may be supplied (i.e. they are exclusive).</remarks>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult"/> structure.</returns>
+ private static ParallelLoopResult ForWorker64<TLocal>(
+ long fromInclusive, long toExclusive,
+ ParallelOptions parallelOptions,
+ Action<long> body,
+ Action<long, ParallelLoopState> bodyWithState,
+ Func<long, ParallelLoopState, TLocal, TLocal> bodyWithLocal,
+ Func<TLocal> localInit, Action<TLocal> localFinally)
+ {
+ Contract.Assert(((body == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) + (bodyWithLocal == null ? 0 : 1)) == 1,
+ "expected exactly one body function to be supplied");
+ Contract.Assert(bodyWithLocal != null || (localInit == null && localFinally == null),
+ "thread local functions should only be supplied for loops w/ thread local bodies");
+
+ // Instantiate our result. Specifics will be filled in later.
+ ParallelLoopResult result = new ParallelLoopResult();
+
+ // We just return immediately if 'to' is smaller (or equal to) 'from'.
+ if (toExclusive <= fromInclusive)
+ {
+ result.m_completed = true;
+ return result;
+ }
+
+ // For all loops we need a shared flag even though we don't have a body with state,
+ // because the shared flag contains the exceptional bool, which triggers other workers
+ // to exit their loops if one worker catches an exception
+ ParallelLoopStateFlags64 sharedPStateFlags = new ParallelLoopStateFlags64();
+
+ TaskCreationOptions creationOptions = TaskCreationOptions.None;
+ InternalTaskOptions internalOptions = InternalTaskOptions.SelfReplicating;
+
+ // Before getting started, do a quick peek to see if we have been canceled already
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ {
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+ }
+
+ // initialize ranges with passed in loop arguments and expected number of workers
+ int numExpectedWorkers = (parallelOptions.EffectiveMaxConcurrencyLevel == -1) ?
+ PlatformHelper.ProcessorCount :
+ parallelOptions.EffectiveMaxConcurrencyLevel;
+ RangeManager rangeManager = new RangeManager(fromInclusive, toExclusive, 1, numExpectedWorkers);
+
+ // Keep track of any cancellations
+ OperationCanceledException oce = null;
+
+ CancellationTokenRegistration ctr = new CancellationTokenRegistration();
+
+ // if cancellation is enabled, we need to register a callback to stop the loop when it gets signaled
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr = parallelOptions.CancellationToken.InternalRegisterWithoutEC((o) =>
+ {
+ // Cause processing to stop
+ sharedPStateFlags.Cancel();
+ // Record our cancellation
+ oce = new OperationCanceledException(parallelOptions.CancellationToken);
+ }, null);
+ }
+
+ // ETW event for Parallel For begin
+ Task callerTask = null;
+ int forkJoinContextID = 0;
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ forkJoinContextID = Interlocked.Increment(ref s_forkJoinContextID);
+ callerTask = Task.InternalCurrent;
+ TplEtwProvider.Log.ParallelLoopBegin((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID, TplEtwProvider.ForkJoinOperationType.ParallelFor,
+ fromInclusive, toExclusive);
+ }
+
+ ParallelForReplicatingTask rootTask = null;
+
+ try
+ {
+ // this needs to be in try-block because it can throw in BuggyScheduler.MaxConcurrencyLevel
+ rootTask = new ParallelForReplicatingTask(
+ parallelOptions,
+ delegate
+ {
+ //
+ // first thing we do upon enterying the task is to register as a new "RangeWorker" with the
+ // shared RangeManager instance.
+ //
+ // If this call returns a RangeWorker struct which wraps the state needed by this task
+ //
+ // We need to call FindNewWork() on it to see whether there's a chunk available.
+ //
+
+ // Cache some information about the current task
+ Task currentWorkerTask = Task.InternalCurrent;
+ bool bIsRootTask = (currentWorkerTask == rootTask);
+
+ RangeWorker currentWorker = new RangeWorker();
+ Object savedStateFromPreviousReplica = currentWorkerTask.SavedStateFromPreviousReplica;
+
+ if (savedStateFromPreviousReplica is RangeWorker)
+ currentWorker = (RangeWorker)savedStateFromPreviousReplica;
+ else
+ currentWorker = rangeManager.RegisterNewWorker();
+
+
+ // These are the local index values to be used in the sequential loop.
+ // Their values filled in by FindNewWork
+ long nFromInclusiveLocal;
+ long nToExclusiveLocal;
+
+ if (currentWorker.FindNewWork(out nFromInclusiveLocal, out nToExclusiveLocal) == false ||
+ sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal) == true)
+ {
+ return; // no need to run
+ }
+
+ // ETW event for ParallelFor Worker Fork
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelFork((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+
+ TLocal localValue = default(TLocal);
+ bool bLocalValueInitialized = false; // Tracks whether localInit ran without exceptions, so that we can skip localFinally if it wasn't
+
+ try
+ {
+
+ // Create a new state object that references the shared "stopped" and "exceptional" flags
+ // If needed, it will contain a new instance of thread-local state by invoking the selector.
+ ParallelLoopState64 state = null;
+
+ if (bodyWithState != null)
+ {
+ Contract.Assert(sharedPStateFlags != null);
+ state = new ParallelLoopState64(sharedPStateFlags);
+ }
+ else if (bodyWithLocal != null)
+ {
+ Contract.Assert(sharedPStateFlags != null);
+ state = new ParallelLoopState64(sharedPStateFlags);
+
+ // If a thread-local selector was supplied, invoke it. Otherwise, use the default.
+ if (localInit != null)
+ {
+ localValue = localInit();
+ bLocalValueInitialized = true;
+ }
+ }
+
+ // initialize a loop timer which will help us decide whether we should exit early
+ LoopTimer loopTimer = new LoopTimer(rootTask.ActiveChildCount);
+
+ // Now perform the loop itself.
+ do
+ {
+ if (body != null)
+ {
+ for (long j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop()); // the no-arg version is used since we have no state
+ j += 1)
+ {
+ body(j);
+ }
+ }
+ else if (bodyWithState != null)
+ {
+ for (long j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop(j));
+ j += 1)
+ {
+ state.CurrentIteration = j;
+ bodyWithState(j, state);
+ }
+ }
+ else
+ {
+ for (long j = nFromInclusiveLocal;
+ j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
+ || !sharedPStateFlags.ShouldExitLoop(j));
+ j += 1)
+ {
+ state.CurrentIteration = j;
+ localValue = bodyWithLocal(j, state, localValue);
+ }
+ }
+
+ // Cooperative multitasking workaround for AppDomain fairness.
+ // Check if allowed loop time is exceeded, if so save current state and return. The self replicating task logic
+ // will detect this, and queue up a replacement task. Note that we don't do this on the root task.
+ if (!bIsRootTask && loopTimer.LimitExceeded())
+ {
+ currentWorkerTask.SavedStateForNextReplica = (object)currentWorker;
+ break;
+ }
+ }
+ // Exit if we can't find new work, or if the loop was stoppped.
+ while (currentWorker.FindNewWork(out nFromInclusiveLocal, out nToExclusiveLocal) &&
+ ((sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE) ||
+ !sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal)));
+ }
+ catch
+ {
+ // if we catch an exception in a worker, we signal the other workers to exit the loop, and we rethrow
+ sharedPStateFlags.SetExceptional();
+ throw;
+ }
+ finally
+ {
+ // If a cleanup function was specified, call it. Otherwise, if the type is
+ // IDisposable, we will invoke Dispose on behalf of the user.
+ if (localFinally != null && bLocalValueInitialized)
+ {
+ localFinally(localValue);
+ }
+
+ // ETW event for ParallelFor Worker Join
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelJoin((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+ }
+ },
+ creationOptions, internalOptions);
+
+ rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler); // might throw TSE
+ rootTask.Wait();
+
+ // If we made a cancellation registration, we need to clean it up now before observing the OCE
+ // Otherwise we could be caught in the middle of a callback, and observe PLS_STOPPED, but oce = null
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // If we got through that with no exceptions, and we were canceled, then
+ // throw our cancellation exception
+ if (oce != null) throw oce;
+ }
+ catch (AggregateException aggExp)
+ {
+ // if we made a cancellation registration, and rootTask.Wait threw, we need to clean it up here
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // see if we can combine it into a single OCE. If not propagate the original exception
+ ThrowIfReducableToSingleOCE(aggExp.InnerExceptions, parallelOptions.CancellationToken);
+ throw;
+ }
+ catch (TaskSchedulerException)
+ {
+ // if we made a cancellation registration, and rootTask.RunSynchronously threw, we need to clean it up here
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+ throw;
+ }
+ finally
+ {
+ int sb_status = sharedPStateFlags.LoopStateFlags;
+ result.m_completed = (sb_status == ParallelLoopStateFlags.PLS_NONE);
+ if ((sb_status & ParallelLoopStateFlags.PLS_BROKEN) != 0)
+ {
+ result.m_lowestBreakIteration = sharedPStateFlags.LowestBreakIteration;
+ }
+
+ if ((rootTask != null) && rootTask.IsCompleted) rootTask.Dispose();
+
+ // ETW event for Parallel For End
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ long nTotalIterations = 0;
+
+ // calculate how many iterations we ran in total
+ if (sb_status == ParallelLoopStateFlags.PLS_NONE)
+ nTotalIterations = toExclusive - fromInclusive;
+ else if ((sb_status & ParallelLoopStateFlags.PLS_BROKEN) != 0)
+ nTotalIterations = sharedPStateFlags.LowestBreakIteration - fromInclusive;
+ else
+ nTotalIterations = -1; //PLS_STOPPED! We can't determine this if we were stopped..
+
+ TplEtwProvider.Log.ParallelLoopEnd((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID, nTotalIterations);
+ }
+ }
+
+ return result;
+ }
+
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the current element as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, s_defaultParallelOptions, body, null, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the current element as a parameter.
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Action<TSource> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, parallelOptions, body, null, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource, ParallelLoopState> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, s_defaultParallelOptions, null, body, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Action<TSource, ParallelLoopState> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, parallelOptions, null, body, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and the current element's index (an Int64).
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource, ParallelLoopState, long> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, s_defaultParallelOptions, null, null, body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and the current element's index (an Int64).
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Action<TSource, ParallelLoopState, long> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForEachWorker<TSource, object>(
+ source, parallelOptions, null, null, body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(IEnumerable<TSource> source, Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ return ForEachWorker<TSource, TLocal>(
+ source, s_defaultParallelOptions, null, null, null, body, null, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(IEnumerable<TSource> source,
+ ParallelOptions parallelOptions, Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForEachWorker<TSource, TLocal>(
+ source, parallelOptions, null, null, null, body, null, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, the current element's index (an Int64), and some local
+ /// state that may be shared amongst iterations that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(IEnumerable<TSource> source, Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> body, Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ return ForEachWorker<TSource, TLocal>(
+ source, s_defaultParallelOptions, null, null, null, null, body, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on an <see cref="T:System.Collections.IEnumerable{TSource}"/>
+ /// in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the data in the source.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// enumerable. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, the current element's index (an Int64), and some local
+ /// state that may be shared amongst iterations that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> body, Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return ForEachWorker<TSource, TLocal>(
+ source, parallelOptions, null, null, null, null, body, localInit, localFinally);
+ }
+
+
+ /// <summary>
+ /// Performs the major work of the parallel foreach loop. It assumes that argument validation has
+ /// already been performed by the caller. This function's whole purpose in life is to enable as much
+ /// reuse of common implementation details for the various For overloads we offer. Without it, we'd
+ /// end up with lots of duplicate code. It handles: (1) simple foreach loops, (2) foreach loops that
+ /// depend on ParallelState, and (3) foreach loops that access indices, (4) foreach loops with thread
+ /// local data, and any necessary permutations thereof.
+ ///
+ /// </summary>
+ /// <typeparam name="TSource">The type of the source data.</typeparam>
+ /// <typeparam name="TLocal">The type of the local data.</typeparam>
+ /// <param name="source">An enumerable data source.</param>
+ /// <param name="parallelOptions">ParallelOptions instance to use with this ForEach-loop</param>
+ /// <param name="body">The simple loop body.</param>
+ /// <param name="bodyWithState">The loop body for ParallelState overloads.</param>
+ /// <param name="bodyWithStateAndIndex">The loop body for ParallelState/indexed overloads.</param>
+ /// <param name="bodyWithStateAndLocal">The loop body for ParallelState/thread local state overloads.</param>
+ /// <param name="bodyWithEverything">The loop body for ParallelState/indexed/thread local state overloads.</param>
+ /// <param name="localInit">A selector function that returns new thread local state.</param>
+ /// <param name="localFinally">A cleanup function to destroy thread local state.</param>
+ /// <remarks>Only one of the bodyXX arguments may be supplied (i.e. they are exclusive).</remarks>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult"/> structure.</returns>
+ private static ParallelLoopResult ForEachWorker<TSource, TLocal>(
+ IEnumerable<TSource> source,
+ ParallelOptions parallelOptions,
+ Action<TSource> body,
+ Action<TSource, ParallelLoopState> bodyWithState,
+ Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
+ Func<TLocal> localInit, Action<TLocal> localFinally)
+ {
+ Contract.Assert(((body == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) +
+ (bodyWithStateAndIndex == null ? 0 : 1) + (bodyWithStateAndLocal == null ? 0 : 1) + (bodyWithEverything == null ? 0 : 1)) == 1,
+ "expected exactly one body function to be supplied");
+ Contract.Assert((bodyWithStateAndLocal != null) || (bodyWithEverything != null) || (localInit == null && localFinally == null),
+ "thread local functions should only be supplied for loops w/ thread local bodies");
+
+ // Before getting started, do a quick peek to see if we have been canceled already
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ {
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+ }
+
+ // If it's an array, we can use a fast-path that uses ldelems in the IL.
+ TSource[] sourceAsArray = source as TSource[];
+ if (sourceAsArray != null)
+ {
+ return ForEachWorker<TSource, TLocal>(
+ sourceAsArray, parallelOptions, body, bodyWithState, bodyWithStateAndIndex, bodyWithStateAndLocal,
+ bodyWithEverything, localInit, localFinally);
+ }
+
+ // If we can index into the list, we can use a faster code-path that doesn't result in
+ // contention for the single, shared enumerator object.
+ IList<TSource> sourceAsList = source as IList<TSource>;
+ if (sourceAsList != null)
+ {
+ return ForEachWorker<TSource, TLocal>(
+ sourceAsList, parallelOptions, body, bodyWithState, bodyWithStateAndIndex, bodyWithStateAndLocal,
+ bodyWithEverything, localInit, localFinally);
+ }
+
+ // This is an honest-to-goodness IEnumerable. Wrap it in a Partitioner and defer to our
+ // ForEach(Partitioner) logic.
+ return PartitionerForEachWorker<TSource, TLocal>(Partitioner.Create(source), parallelOptions, body, bodyWithState,
+ bodyWithStateAndIndex, bodyWithStateAndLocal, bodyWithEverything, localInit, localFinally);
+
+ }
+
+ /// <summary>
+ /// A fast path for the more general ForEachWorker method above. This uses ldelem instructions to
+ /// access the individual elements of the array, which will be faster.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the source data.</typeparam>
+ /// <typeparam name="TLocal">The type of the local data.</typeparam>
+ /// <param name="array">An array data source.</param>
+ /// <param name="parallelOptions">The options to use for execution.</param>
+ /// <param name="body">The simple loop body.</param>
+ /// <param name="bodyWithState">The loop body for ParallelState overloads.</param>
+ /// <param name="bodyWithStateAndIndex">The loop body for indexed/ParallelLoopState overloads.</param>
+ /// <param name="bodyWithStateAndLocal">The loop body for local/ParallelLoopState overloads.</param>
+ /// <param name="bodyWithEverything">The loop body for the most generic overload.</param>
+ /// <param name="localInit">A selector function that returns new thread local state.</param>
+ /// <param name="localFinally">A cleanup function to destroy thread local state.</param>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult"/> structure.</returns>
+ private static ParallelLoopResult ForEachWorker<TSource, TLocal>(
+ TSource[] array,
+ ParallelOptions parallelOptions,
+ Action<TSource> body,
+ Action<TSource, ParallelLoopState> bodyWithState,
+ Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
+ Func<TLocal> localInit, Action<TLocal> localFinally)
+ {
+ Contract.Assert(array != null);
+ Contract.Assert(parallelOptions != null, "ForEachWorker(array): parallelOptions is null");
+
+ int from = array.GetLowerBound(0);
+ int to = array.GetUpperBound(0) + 1;
+
+ if (body != null)
+ {
+ return ForWorker<object>(
+ from, to, parallelOptions, (i) => body(array[i]), null, null, null, null);
+ }
+ else if (bodyWithState != null)
+ {
+ return ForWorker<object>(
+ from, to, parallelOptions, null, (i, state) => bodyWithState(array[i], state), null, null, null);
+ }
+ else if (bodyWithStateAndIndex != null)
+ {
+ return ForWorker<object>(
+ from, to, parallelOptions, null, (i, state) => bodyWithStateAndIndex(array[i], state, i), null, null, null);
+ }
+ else if (bodyWithStateAndLocal != null)
+ {
+ return ForWorker<TLocal>(
+ from, to, parallelOptions, null, null, (i, state, local) => bodyWithStateAndLocal(array[i], state, local), localInit, localFinally);
+ }
+ else
+ {
+ return ForWorker<TLocal>(
+ from, to, parallelOptions, null, null, (i, state, local) => bodyWithEverything(array[i], state, i, local), localInit, localFinally);
+ }
+ }
+
+ /// <summary>
+ /// A fast path for the more general ForEachWorker method above. This uses IList&lt;T&gt;'s indexer
+ /// capabilities to access the individual elements of the list rather than an enumerator.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the source data.</typeparam>
+ /// <typeparam name="TLocal">The type of the local data.</typeparam>
+ /// <param name="list">A list data source.</param>
+ /// <param name="parallelOptions">The options to use for execution.</param>
+ /// <param name="body">The simple loop body.</param>
+ /// <param name="bodyWithState">The loop body for ParallelState overloads.</param>
+ /// <param name="bodyWithStateAndIndex">The loop body for indexed/ParallelLoopState overloads.</param>
+ /// <param name="bodyWithStateAndLocal">The loop body for local/ParallelLoopState overloads.</param>
+ /// <param name="bodyWithEverything">The loop body for the most generic overload.</param>
+ /// <param name="localInit">A selector function that returns new thread local state.</param>
+ /// <param name="localFinally">A cleanup function to destroy thread local state.</param>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult"/> structure.</returns>
+ private static ParallelLoopResult ForEachWorker<TSource, TLocal>(
+ IList<TSource> list,
+ ParallelOptions parallelOptions,
+ Action<TSource> body,
+ Action<TSource, ParallelLoopState> bodyWithState,
+ Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
+ Func<TLocal> localInit, Action<TLocal> localFinally)
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(parallelOptions != null, "ForEachWorker(list): parallelOptions is null");
+
+ if (body != null)
+ {
+ return ForWorker<object>(
+ 0, list.Count, parallelOptions, (i) => body(list[i]), null, null, null, null);
+ }
+ else if (bodyWithState != null)
+ {
+ return ForWorker<object>(
+ 0, list.Count, parallelOptions, null, (i, state) => bodyWithState(list[i], state), null, null, null);
+ }
+ else if (bodyWithStateAndIndex != null)
+ {
+ return ForWorker<object>(
+ 0, list.Count, parallelOptions, null, (i, state) => bodyWithStateAndIndex(list[i], state, i), null, null, null);
+ }
+ else if (bodyWithStateAndLocal != null)
+ {
+ return ForWorker<TLocal>(
+ 0, list.Count, parallelOptions, null, null, (i, state, local) => bodyWithStateAndLocal(list[i], state, local), localInit, localFinally);
+ }
+ else
+ {
+ return ForWorker<TLocal>(
+ 0, list.Count, parallelOptions, null, null, (i, state, local) => bodyWithEverything(list[i], state, i, local), localInit, localFinally);
+ }
+ }
+
+
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the current element as a parameter.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ Partitioner<TSource> source,
+ Action<TSource> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, s_defaultParallelOptions, body, null, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ Partitioner<TSource> source,
+ Action<TSource, ParallelLoopState> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, s_defaultParallelOptions, null, body, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.OrderablePartitioner{TSource}">
+ /// OrderablePartitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The OrderablePartitioner that contains the original data source.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// KeysNormalized property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> OrderablePartitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner do not return the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IList with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() or GetDynamicOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and the current element's index (an Int64).
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ OrderablePartitioner<TSource> source,
+ Action<TSource, ParallelLoopState, long> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+
+ if (!source.KeysNormalized)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_OrderedPartitionerKeysNotNormalized"));
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, s_defaultParallelOptions, null, null, body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(
+ Partitioner<TSource> source,
+ Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ return PartitionerForEachWorker<TSource, TLocal>(source, s_defaultParallelOptions, null, null, null, body, null, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.OrderablePartitioner{TSource}">
+ /// OrderablePartitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">The OrderablePartitioner that contains the original data source.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// KeysNormalized property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> OrderablePartitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner do not return the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IList with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() or GetDynamicOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, the current element's index (an Int64), and some local
+ /// state that may be shared amongst iterations that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(
+ OrderablePartitioner<TSource> source,
+ Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+
+ if (!source.KeysNormalized)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_OrderedPartitionerKeysNotNormalized"));
+ }
+
+ return PartitionerForEachWorker<TSource, TLocal>(source, s_defaultParallelOptions, null, null, null, null, body, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the current element as a parameter.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ Partitioner<TSource> source,
+ ParallelOptions parallelOptions,
+ Action<TSource> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, parallelOptions, body, null, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// and a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ Partitioner<TSource> source,
+ ParallelOptions parallelOptions,
+ Action<TSource, ParallelLoopState> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, parallelOptions, null, body, null, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.OrderablePartitioner{TSource}">
+ /// OrderablePartitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <param name="source">The OrderablePartitioner that contains the original data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// KeysNormalized property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> OrderablePartitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner do not return the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IList with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() or GetDynamicOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and the current element's index (an Int64).
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource>(
+ OrderablePartitioner<TSource> source,
+ ParallelOptions parallelOptions,
+ Action<TSource, ParallelLoopState, long> body)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ if (!source.KeysNormalized)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_OrderedPartitionerKeysNotNormalized"));
+ }
+
+ return PartitionerForEachWorker<TSource, object>(source, parallelOptions, null, null, body, null, null, null, null);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">
+ /// Partitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">The Partitioner that contains the original data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> Partitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> Partitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner does not return
+ /// the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() method in the <paramref name="source"/> Partitioner returns an IList
+ /// with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() method in the <paramref name="source"/> Partitioner returns an
+ /// IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, and some local state that may be shared amongst iterations
+ /// that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(
+ Partitioner<TSource> source,
+ ParallelOptions parallelOptions,
+ Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ return PartitionerForEachWorker<TSource, TLocal>(source, parallelOptions, null, null, null, body, null, localInit, localFinally);
+ }
+
+ /// <summary>
+ /// Executes a for each operation on a <see cref="T:System.Collections.Concurrent.OrderablePartitioner{TSource}">
+ /// OrderablePartitioner</see> in which iterations may run in parallel.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements in <paramref name="source"/>.</typeparam>
+ /// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
+ /// <param name="source">The OrderablePartitioner that contains the original data source.</param>
+ /// <param name="parallelOptions">A <see cref="T:System.Threading.Tasks.ParallelOptions">ParallelOptions</see>
+ /// instance that configures the behavior of this operation.</param>
+ /// <param name="localInit">The function delegate that returns the initial state of the local data
+ /// for each thread.</param>
+ /// <param name="body">The delegate that is invoked once per iteration.</param>
+ /// <param name="localFinally">The delegate that performs a final action on the local state of each
+ /// thread.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="source"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="parallelOptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="body"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localInit"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="localFinally"/> argument is null.</exception>
+ /// <exception cref="T:System.OperationCanceledException">The exception that is thrown when the
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the <paramref name="parallelOptions"/>
+ /// argument is set</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// SupportsDynamicPartitions property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// KeysNormalized property in the <paramref name="source"/> OrderablePartitioner returns
+ /// false.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when any
+ /// methods in the <paramref name="source"/> OrderablePartitioner return null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner do not return the correct number of partitions.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetPartitions() or GetOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IList with at least one null value.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The exception that is thrown when the
+ /// GetDynamicPartitions() or GetDynamicOrderablePartitions() methods in the <paramref name="source"/>
+ /// OrderablePartitioner return an IEnumerable whose GetEnumerator() method returns null.</exception>
+ /// <exception cref="T:System.AggregateException">The exception that is thrown to contain an exception
+ /// thrown from one of the specified delegates.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The exception that is thrown when the
+ /// the <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> associated with the
+ /// the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> in the
+ /// <paramref name="parallelOptions"/> has been disposed.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.ParallelLoopResult">ParallelLoopResult</see> structure
+ /// that contains information on what portion of the loop completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="T:System.Collections.Concurrent.Partitioner{TSource}">Partitioner</see> is used to retrieve
+ /// the elements to be processed, in place of the original data source. If the current element's
+ /// index is desired, the source must be an <see cref="T:System.Collections.Concurrent.OrderablePartitioner">
+ /// OrderablePartitioner</see>.
+ /// </para>
+ /// <para>
+ /// The <paramref name="body"/> delegate is invoked once for each element in the <paramref name="source"/>
+ /// Partitioner. It is provided with the following parameters: the current element,
+ /// a <see cref="System.Threading.Tasks.ParallelLoopState">ParallelLoopState</see> instance that may be
+ /// used to break out of the loop prematurely, the current element's index (an Int64), and some local
+ /// state that may be shared amongst iterations that execute on the same thread.
+ /// </para>
+ /// <para>
+ /// The <paramref name="localInit"/> delegate is invoked once for each thread that participates in the loop's
+ /// execution and returns the initial local state for each of those threads. These initial states are passed to the first
+ /// <paramref name="body"/> invocations on each thread. Then, every subsequent body invocation returns a possibly
+ /// modified state value that is passed to the next body invocation. Finally, the last body invocation on each thread returns a state value
+ /// that is passed to the <paramref name="localFinally"/> delegate. The localFinally delegate is invoked once per thread to perform a final
+ /// action on each thread's local state.
+ /// </para>
+ /// </remarks>
+ public static ParallelLoopResult ForEach<TSource, TLocal>(
+ OrderablePartitioner<TSource> source,
+ ParallelOptions parallelOptions,
+ Func<TLocal> localInit,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> body,
+ Action<TLocal> localFinally)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+ if (body == null)
+ {
+ throw new ArgumentNullException("body");
+ }
+ if (localInit == null)
+ {
+ throw new ArgumentNullException("localInit");
+ }
+ if (localFinally == null)
+ {
+ throw new ArgumentNullException("localFinally");
+ }
+ if (parallelOptions == null)
+ {
+ throw new ArgumentNullException("parallelOptions");
+ }
+
+ if (!source.KeysNormalized)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_OrderedPartitionerKeysNotNormalized"));
+ }
+
+ return PartitionerForEachWorker<TSource, TLocal>(source, parallelOptions, null, null, null, null, body, localInit, localFinally);
+ }
+
+ // Main worker method for Parallel.ForEach() calls w/ Partitioners.
+ private static ParallelLoopResult PartitionerForEachWorker<TSource, TLocal>(
+ Partitioner<TSource> source, // Might be OrderablePartitioner
+ ParallelOptions parallelOptions,
+ Action<TSource> simpleBody,
+ Action<TSource, ParallelLoopState> bodyWithState,
+ Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
+ Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
+ Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
+ Func<TLocal> localInit,
+ Action<TLocal> localFinally)
+ {
+ Contract.Assert(((simpleBody == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) +
+ (bodyWithStateAndIndex == null ? 0 : 1) + (bodyWithStateAndLocal == null ? 0 : 1) + (bodyWithEverything == null ? 0 : 1)) == 1,
+ "PartitionForEach: expected exactly one body function to be supplied");
+ Contract.Assert((bodyWithStateAndLocal != null) || (bodyWithEverything != null) || (localInit == null && localFinally == null),
+ "PartitionForEach: thread local functions should only be supplied for loops w/ thread local bodies");
+
+ OrderablePartitioner<TSource> orderedSource = source as OrderablePartitioner<TSource>;
+ Contract.Assert((orderedSource != null) || (bodyWithStateAndIndex == null && bodyWithEverything == null),
+ "PartitionForEach: bodies with indices are only allowable for OrderablePartitioner");
+
+ if (!source.SupportsDynamicPartitions)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_PartitionerNotDynamic"));
+ }
+
+ // Before getting started, do a quick peek to see if we have been canceled already
+ if (parallelOptions.CancellationToken.IsCancellationRequested)
+ {
+ throw new OperationCanceledException(parallelOptions.CancellationToken);
+ }
+
+ // ETW event for Parallel For begin
+ int forkJoinContextID = 0;
+ Task callerTask = null;
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ forkJoinContextID = Interlocked.Increment(ref s_forkJoinContextID);
+ callerTask = Task.InternalCurrent;
+ TplEtwProvider.Log.ParallelLoopBegin((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID, TplEtwProvider.ForkJoinOperationType.ParallelForEach,
+ 0, 0);
+ }
+
+ // For all loops we need a shared flag even though we don't have a body with state,
+ // because the shared flag contains the exceptional bool, which triggers other workers
+ // to exit their loops if one worker catches an exception
+ ParallelLoopStateFlags64 sharedPStateFlags = new ParallelLoopStateFlags64();
+
+ // Instantiate our result. Specifics will be filled in later.
+ ParallelLoopResult result = new ParallelLoopResult();
+
+ // Keep track of any cancellations
+ OperationCanceledException oce = null;
+
+ CancellationTokenRegistration ctr = new CancellationTokenRegistration();
+
+ // if cancellation is enabled, we need to register a callback to stop the loop when it gets signaled
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr = parallelOptions.CancellationToken.InternalRegisterWithoutEC((o) =>
+ {
+ // Cause processing to stop
+ sharedPStateFlags.Cancel();
+ // Record our cancellation
+ oce = new OperationCanceledException(parallelOptions.CancellationToken);
+ }, null);
+ }
+
+ // Get our dynamic partitioner -- depends on whether source is castable to OrderablePartitioner
+ // Also, do some error checking.
+ IEnumerable<TSource> partitionerSource = null;
+ IEnumerable<KeyValuePair<long, TSource>> orderablePartitionerSource = null;
+ if (orderedSource != null)
+ {
+ orderablePartitionerSource = orderedSource.GetOrderableDynamicPartitions();
+ if (orderablePartitionerSource == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_PartitionerReturnedNull"));
+ }
+ }
+ else
+ {
+ partitionerSource = source.GetDynamicPartitions();
+ if (partitionerSource == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_PartitionerReturnedNull"));
+ }
+ }
+
+ ParallelForReplicatingTask rootTask = null;
+
+ // This is the action that will be run by each replicable task.
+ Action partitionAction = delegate
+ {
+ Task currentWorkerTask = Task.InternalCurrent;
+
+ // ETW event for ParallelForEach Worker Fork
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelFork((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+
+ TLocal localValue = default(TLocal);
+ bool bLocalValueInitialized = false; // Tracks whether localInit ran without exceptions, so that we can skip localFinally if it wasn't
+ IDisposable myPartitionToDispose = null;
+
+ try
+ {
+ // Create a new state object that references the shared "stopped" and "exceptional" flags.
+ // If needed, it will contain a new instance of thread-local state by invoking the selector.
+ ParallelLoopState64 state = null;
+
+ if (bodyWithState != null || bodyWithStateAndIndex != null)
+ {
+ state = new ParallelLoopState64(sharedPStateFlags);
+ }
+ else if (bodyWithStateAndLocal != null || bodyWithEverything != null)
+ {
+ state = new ParallelLoopState64(sharedPStateFlags);
+ // If a thread-local selector was supplied, invoke it. Otherwise, stick with the default.
+ if (localInit != null)
+ {
+ localValue = localInit();
+ bLocalValueInitialized = true;
+ }
+ }
+
+
+ bool bIsRootTask = (rootTask == currentWorkerTask);
+
+ // initialize a loop timer which will help us decide whether we should exit early
+ LoopTimer loopTimer = new LoopTimer(rootTask.ActiveChildCount);
+
+ if (orderedSource != null)
+ {
+ // Use this path for OrderablePartitioner
+
+
+ // first check if there's saved state from a previous replica that we might be replacing.
+ // the only state to be passed down in such a transition is the enumerator
+ IEnumerator<KeyValuePair<long, TSource>> myPartition = currentWorkerTask.SavedStateFromPreviousReplica as IEnumerator<KeyValuePair<long, TSource>>;
+ if (myPartition == null)
+ {
+ // apparently we're a brand new replica, get a fresh enumerator from the partitioner
+ myPartition = orderablePartitionerSource.GetEnumerator();
+ if (myPartition == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_NullEnumerator"));
+ }
+ }
+ myPartitionToDispose = myPartition;
+
+ while (myPartition.MoveNext())
+ {
+ KeyValuePair<long, TSource> kvp = myPartition.Current;
+ long index = kvp.Key;
+ TSource value = kvp.Value;
+
+ // Update our iteration index
+ if (state != null) state.CurrentIteration = index;
+
+ if (simpleBody != null)
+ simpleBody(value);
+ else if (bodyWithState != null)
+ bodyWithState(value, state);
+ else if (bodyWithStateAndIndex != null)
+ bodyWithStateAndIndex(value, state, index);
+ else if (bodyWithStateAndLocal != null)
+ localValue = bodyWithStateAndLocal(value, state, localValue);
+ else
+ localValue = bodyWithEverything(value, state, index, localValue);
+
+ if (sharedPStateFlags.ShouldExitLoop(index)) break;
+
+ // Cooperative multitasking workaround for AppDomain fairness.
+ // Check if allowed loop time is exceeded, if so save current state and return. The self replicating task logic
+ // will detect this, and queue up a replacement task. Note that we don't do this on the root task.
+ if (!bIsRootTask && loopTimer.LimitExceeded())
+ {
+ currentWorkerTask.SavedStateForNextReplica = myPartition;
+ myPartitionToDispose = null;
+ break;
+ }
+ }
+
+ }
+ else
+ {
+ // Use this path for Partitioner that is not OrderablePartitioner
+
+ // first check if there's saved state from a previous replica that we might be replacing.
+ // the only state to be passed down in such a transition is the enumerator
+ IEnumerator<TSource> myPartition = currentWorkerTask.SavedStateFromPreviousReplica as IEnumerator<TSource>;
+ if (myPartition == null)
+ {
+ // apparently we're a brand new replica, get a fresh enumerator from the partitioner
+ myPartition = partitionerSource.GetEnumerator();
+ if (myPartition == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Parallel_ForEach_NullEnumerator"));
+ }
+ }
+ myPartitionToDispose = myPartition;
+
+ // I'm not going to try to maintain this
+ if (state != null)
+ state.CurrentIteration = 0;
+
+ while (myPartition.MoveNext())
+ {
+ TSource t = myPartition.Current;
+
+ if (simpleBody != null)
+ simpleBody(t);
+ else if (bodyWithState != null)
+ bodyWithState(t, state);
+ else if (bodyWithStateAndLocal != null)
+ localValue = bodyWithStateAndLocal(t, state, localValue);
+ else
+ Contract.Assert(false, "PartitionerForEach: illegal body type in Partitioner handler");
+
+
+ // Any break, stop or exception causes us to halt
+ // We don't have the global indexing information to discriminate whether or not
+ // we are before or after a break point.
+ if (sharedPStateFlags.LoopStateFlags != ParallelLoopStateFlags.PLS_NONE)
+ break;
+
+ // Cooperative multitasking workaround for AppDomain fairness.
+ // Check if allowed loop time is exceeded, if so save current state and return. The self replicating task logic
+ // will detect this, and queue up a replacement task. Note that we don't do this on the root task.
+ if (!bIsRootTask && loopTimer.LimitExceeded())
+ {
+ currentWorkerTask.SavedStateForNextReplica = myPartition;
+ myPartitionToDispose = null;
+ break;
+ }
+ }
+ }
+ }
+ catch
+ {
+ // Inform other tasks of the exception, then rethrow
+ sharedPStateFlags.SetExceptional();
+ throw;
+ }
+ finally
+ {
+ if (localFinally != null && bLocalValueInitialized)
+ {
+ localFinally(localValue);
+ }
+
+ if (myPartitionToDispose != null)
+ {
+ myPartitionToDispose.Dispose();
+ }
+
+ // ETW event for ParallelFor Worker Join
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelJoin((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
+ forkJoinContextID);
+ }
+ }
+ };
+
+ try
+ {
+ // Create and start the self-replicating task.
+ // This needs to be in try-block because it can throw in BuggyScheduler.MaxConcurrencyLevel
+ rootTask = new ParallelForReplicatingTask(parallelOptions, partitionAction, TaskCreationOptions.None,
+ InternalTaskOptions.SelfReplicating);
+
+ // And process it's completion...
+ // Moved inside try{} block because faulty scheduler may throw here.
+ rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler);
+
+ rootTask.Wait();
+
+ // If we made a cancellation registration, we need to clean it up now before observing the OCE
+ // Otherwise we could be caught in the middle of a callback, and observe PLS_STOPPED, but oce = null
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // If we got through that with no exceptions, and we were canceled, then
+ // throw our cancellation exception
+ if (oce != null) throw oce;
+ }
+ catch (AggregateException aggExp)
+ {
+ // if we made a cancellation registration, and rootTask.Wait threw, we need to clean it up here
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+
+ // see if we can combine it into a single OCE. If not propagate the original exception
+ ThrowIfReducableToSingleOCE(aggExp.InnerExceptions, parallelOptions.CancellationToken);
+ throw;
+ }
+ catch (TaskSchedulerException)
+ {
+ // if we made a cancellation registration, and either we threw an exception constructing rootTask or
+ // rootTask.RunSynchronously threw, we need to clean it up here.
+ if (parallelOptions.CancellationToken.CanBeCanceled)
+ {
+ ctr.Dispose();
+ }
+ throw;
+ }
+ finally
+ {
+ int sb_status = sharedPStateFlags.LoopStateFlags;
+ result.m_completed = (sb_status == ParallelLoopStateFlags.PLS_NONE);
+ if ((sb_status & ParallelLoopStateFlags.PLS_BROKEN) != 0)
+ {
+ result.m_lowestBreakIteration = sharedPStateFlags.LowestBreakIteration;
+ }
+
+ if ((rootTask != null) && rootTask.IsCompleted) rootTask.Dispose();
+
+ //dispose the partitioner source if it implements IDisposable
+ IDisposable d = null;
+ if (orderablePartitionerSource != null)
+ {
+ d = orderablePartitionerSource as IDisposable;
+ }
+ else
+ {
+ d = partitionerSource as IDisposable;
+ }
+
+ if (d != null)
+ {
+ d.Dispose();
+ }
+
+ // ETW event for Parallel For End
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ TplEtwProvider.Log.ParallelLoopEnd((callerTask != null ? callerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (callerTask != null ? callerTask.Id : 0),
+ forkJoinContextID, 0);
+ }
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Internal utility function that implements the OCE filtering behavior for all Parallel.* APIs.
+ /// Throws a single OperationCancelledException object with the token if the Exception collection only contains
+ /// OperationCancelledExceptions with the given CancellationToken.
+ ///
+ /// </summary>
+ /// <param name="excCollection"> The exception collection to filter</param>
+ /// <param name="ct"> The CancellationToken expected on all inner exceptions</param>
+ /// <returns></returns>
+ internal static void ThrowIfReducableToSingleOCE(IEnumerable<Exception> excCollection, CancellationToken ct)
+ {
+ bool bCollectionNotZeroLength = false;
+ if (ct.IsCancellationRequested)
+ {
+ foreach (Exception e in excCollection)
+ {
+ bCollectionNotZeroLength = true;
+ OperationCanceledException oce = e as OperationCanceledException;
+ if (oce == null || oce.CancellationToken != ct)
+ {
+ // mismatch found
+ return;
+ }
+ }
+
+ // all exceptions are OCEs with this token, let's throw it
+ if (bCollectionNotZeroLength) throw new OperationCanceledException(ct);
+ }
+ }
+
+ internal struct LoopTimer
+ {
+ public LoopTimer(int nWorkerTaskIndex)
+ {
+ // This logic ensures that we have a diversity of timeouts across worker tasks (100, 150, 200, 250, 100, etc)
+ // Otherwise all worker will try to timeout at precisely the same point, which is bad if the work is just about to finish
+ int timeOut = s_BaseNotifyPeriodMS + (nWorkerTaskIndex % PlatformHelper.ProcessorCount) * s_NotifyPeriodIncrementMS;
+
+ m_timeLimit = Environment.TickCount + timeOut;
+ }
+
+ public bool LimitExceeded()
+ {
+ Contract.Assert(m_timeLimit != 0, "Probably the default initializer for LoopTimer was used somewhere");
+
+ // comparing against the next expected time saves an addition operation here
+ // Also we omit the comparison for wrap around here. The only side effect is one extra early yield every 38 days.
+ return (Environment.TickCount > m_timeLimit);
+ }
+
+ const int s_BaseNotifyPeriodMS = 100;
+ const int s_NotifyPeriodIncrementMS = 50;
+
+ private int m_timeLimit;
+ }
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/ParallelLoopState.cs b/src/mscorlib/src/System/Threading/Tasks/ParallelLoopState.cs
new file mode 100644
index 0000000000..4db3a9d105
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/ParallelLoopState.cs
@@ -0,0 +1,642 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ParallelState.cs
+//
+//
+// A non-generic and generic parallel state class, used by the Parallel helper class
+// for parallel loop management.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+
+// Prevents compiler warnings/errors regarding the use of ref params in Interlocked methods
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+
+ /// <summary>
+ /// Enables iterations of <see cref="T:System.Threading.Tasks.Parallel"/> loops to interact with
+ /// other iterations.
+ /// </summary>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerDisplay("ShouldExitCurrentIteration = {ShouldExitCurrentIteration}")]
+ public class ParallelLoopState
+ {
+ // Derived classes will track a ParallelStateFlags32 or ParallelStateFlags64.
+ // So this is slightly redundant, but it enables us to implement some
+ // methods in this base class.
+ private ParallelLoopStateFlags m_flagsBase;
+
+ internal ParallelLoopState(ParallelLoopStateFlags fbase)
+ {
+ m_flagsBase = fbase;
+ }
+
+ /// <summary>
+ /// Internal/virtual support for ShouldExitCurrentIteration.
+ /// </summary>
+ internal virtual bool InternalShouldExitCurrentIteration
+ {
+ get
+ {
+ Contract.Assert(false);
+ throw new NotSupportedException(
+ Environment.GetResourceString("ParallelState_NotSupportedException_UnsupportedMethod"));
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the current iteration of the loop should exit based
+ /// on requests made by this or other iterations.
+ /// </summary>
+ /// <remarks>
+ /// When an iteration of a loop calls <see cref="Break()"/> or <see cref="Stop()"/>, or
+ /// when one throws an exception, or when the loop is canceled, the <see cref="Parallel"/> class will proactively
+ /// attempt to prohibit additional iterations of the loop from starting execution.
+ /// However, there may be cases where it is unable to prevent additional iterations from starting.
+ /// It may also be the case that a long-running iteration has already begun execution. In such
+ /// cases, iterations may explicitly check the <see cref="ShouldExitCurrentIteration"/> property and
+ /// cease execution if the property returns true.
+ /// </remarks>
+ public bool ShouldExitCurrentIteration
+ {
+ get
+ {
+ return InternalShouldExitCurrentIteration;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether any iteration of the loop has called <see cref="Stop()"/>.
+ /// </summary>
+ public bool IsStopped
+ {
+ get
+ {
+ return ((m_flagsBase.LoopStateFlags & ParallelLoopStateFlags.PLS_STOPPED) != 0);
+ }
+ }
+
+ /// <summary>
+ /// Gets whether any iteration of the loop has thrown an exception that went unhandled by that
+ /// iteration.
+ /// </summary>
+ public bool IsExceptional
+ {
+ get
+ {
+ return ((m_flagsBase.LoopStateFlags & ParallelLoopStateFlags.PLS_EXCEPTIONAL) != 0);
+ }
+ }
+
+ /// <summary>
+ /// Internal/virtual support for LowestBreakIteration.
+ /// </summary>
+ internal virtual long? InternalLowestBreakIteration
+ {
+ get
+ {
+ Contract.Assert(false);
+ throw new NotSupportedException(
+ Environment.GetResourceString("ParallelState_NotSupportedException_UnsupportedMethod"));
+ }
+ }
+
+ /// <summary>
+ /// Gets the lowest iteration of the loop from which <see cref="Break()"/> was called.
+ /// </summary>
+ /// <remarks>
+ /// If no iteration of the loop called <see cref="Break()"/>, this property will return null.
+ /// </remarks>
+ public long? LowestBreakIteration
+ {
+ get
+ {
+ return InternalLowestBreakIteration;
+ }
+ }
+
+ /// <summary>
+ /// Communicates that the <see cref="Parallel"/> loop should cease execution at the system's earliest
+ /// convenience.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The <see cref="Break()"/> method was previously called. <see cref="Break()"/> and <see
+ /// cref="Stop()"/> may not be used in combination by iterations of the same loop.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// <see cref="Stop()"/> may be used to communicate to the loop that no other iterations need be run.
+ /// For long-running iterations that may already be executing, <see cref="Stop()"/> causes <see cref="IsStopped"/>
+ /// to return true for all other iterations of the loop, such that another iteration may check <see
+ /// cref="IsStopped"/> and exit early if it's observed to be true.
+ /// </para>
+ /// <para>
+ /// <see cref="Stop()"/> is typically employed in search-based algorithms, where once a result is found,
+ /// no other iterations need be executed.
+ /// </para>
+ /// </remarks>
+ public void Stop()
+ {
+ m_flagsBase.Stop();
+ }
+
+ // Internal/virtual support for Break().
+ internal virtual void InternalBreak()
+ {
+ Contract.Assert(false);
+ throw new NotSupportedException(
+ Environment.GetResourceString("ParallelState_NotSupportedException_UnsupportedMethod"));
+ }
+
+ /// <summary>
+ /// Communicates that the <see cref="Parallel"/> loop should cease execution at the system's earliest
+ /// convenience of iterations beyond the current iteration.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The <see cref="Stop()"/> method was previously called. <see cref="Break()"/> and <see cref="Stop()"/>
+ /// may not be used in combination by iterations of the same loop.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// <see cref="Break()"/> may be used to communicate to the loop that no other iterations after the
+ /// current iteration need be run. For example, if <see cref="Break()"/> is called from the 100th
+ /// iteration of a for loop iterating in parallel from 0 to 1000, all iterations less than 100 should
+ /// still be run, but the iterations from 101 through to 1000 are not necessary.
+ /// </para>
+ /// <para>
+ /// For long-running iterations that may already be executing, <see cref="Break()"/> causes <see
+ /// cref="LowestBreakIteration"/>
+ /// to be set to the current iteration's index if the current index is less than the current value of
+ /// <see cref="LowestBreakIteration"/>.
+ /// </para>
+ /// <para>
+ /// <see cref="Break()"/> is typically employed in search-based algorithms where an ordering is
+ /// present in the data source.
+ /// </para>
+ /// </remarks>
+ public void Break()
+ {
+ InternalBreak();
+ }
+
+ // Helper method to avoid repeating Break() logic between ParallelState32 and ParallelState32<TLocal>
+ internal static void Break(int iteration, ParallelLoopStateFlags32 pflags)
+ {
+ int oldValue = ParallelLoopStateFlags.PLS_NONE;
+
+ // Attempt to change state from "not stopped or broken or canceled or exceptional" to "broken".
+ if (!pflags.AtomicLoopStateUpdate(ParallelLoopStateFlags.PLS_BROKEN,
+ ParallelLoopStateFlags.PLS_STOPPED | ParallelLoopStateFlags.PLS_EXCEPTIONAL | ParallelLoopStateFlags.PLS_CANCELED,
+ ref oldValue))
+ {
+
+ // If we were already stopped, we have a problem
+ if ((oldValue & ParallelLoopStateFlags.PLS_STOPPED) != 0)
+ {
+ throw new InvalidOperationException(
+ Environment.GetResourceString("ParallelState_Break_InvalidOperationException_BreakAfterStop"));
+ }
+ else
+ {
+ // Apparently we previously got cancelled or became exceptional. No action necessary
+ return;
+ }
+ }
+
+ // replace shared LowestBreakIteration with CurrentIteration, but only if CurrentIteration
+ // is less than LowestBreakIteration.
+ int oldLBI = pflags.m_lowestBreakIteration;
+ if (iteration < oldLBI)
+ {
+ SpinWait wait = new SpinWait();
+ while (Interlocked.CompareExchange(
+ ref pflags.m_lowestBreakIteration,
+ iteration,
+ oldLBI) != oldLBI)
+ {
+ wait.SpinOnce();
+ oldLBI = pflags.m_lowestBreakIteration;
+ if (iteration > oldLBI) break;
+ }
+ }
+
+ }
+
+ // Helper method to avoid repeating Break() logic between ParallelState64 and ParallelState64<TLocal>
+ internal static void Break(long iteration, ParallelLoopStateFlags64 pflags)
+ {
+ int oldValue = ParallelLoopStateFlags.PLS_NONE;
+
+ // Attempt to change state from "not stopped or broken or canceled or exceptional" to "broken".
+ if (!pflags.AtomicLoopStateUpdate(ParallelLoopStateFlags.PLS_BROKEN,
+ ParallelLoopStateFlags.PLS_STOPPED | ParallelLoopStateFlags.PLS_EXCEPTIONAL | ParallelLoopStateFlags.PLS_CANCELED,
+ ref oldValue))
+ {
+
+ // If we were already stopped, we have a problem
+ if ((oldValue & ParallelLoopStateFlags.PLS_STOPPED) != 0)
+ {
+ throw new InvalidOperationException(
+ Environment.GetResourceString("ParallelState_Break_InvalidOperationException_BreakAfterStop"));
+ }
+ else
+ {
+ // Apparently we previously got cancelled or became exceptional. No action necessary
+ return;
+ }
+ }
+
+ // replace shared LowestBreakIteration with CurrentIteration, but only if CurrentIteration
+ // is less than LowestBreakIteration.
+ long oldLBI = pflags.LowestBreakIteration;
+ if (iteration < oldLBI)
+ {
+ SpinWait wait = new SpinWait();
+ while (Interlocked.CompareExchange(
+ ref pflags.m_lowestBreakIteration,
+ iteration,
+ oldLBI) != oldLBI)
+ {
+ wait.SpinOnce();
+ oldLBI = pflags.LowestBreakIteration;
+ if (iteration > oldLBI) break;
+ }
+ }
+
+ }
+ }
+
+ internal class ParallelLoopState32 : ParallelLoopState
+ {
+ private ParallelLoopStateFlags32 m_sharedParallelStateFlags;
+ private int m_currentIteration = 0;
+
+ /// <summary>
+ /// Internal constructor to ensure an instance isn't created by users.
+ /// </summary>
+ /// <param name="sharedParallelStateFlags">A flag shared among all threads participating
+ /// in the execution of a certain loop.</param>
+ internal ParallelLoopState32(ParallelLoopStateFlags32 sharedParallelStateFlags)
+ : base(sharedParallelStateFlags)
+ {
+ m_sharedParallelStateFlags = sharedParallelStateFlags;
+ }
+
+ /// <summary>
+ /// Tracks the current loop iteration for the owning task.
+ /// This is used to compute whether or not the task should
+ /// terminate early due to a Break() call.
+ /// </summary>
+ internal int CurrentIteration {
+ get { return m_currentIteration; }
+ set { m_currentIteration = value; }
+ }
+
+ /// <summary>
+ /// Returns true if we should be exiting from the current iteration
+ /// due to Stop(), Break() or exception.
+ /// </summary>
+ internal override bool InternalShouldExitCurrentIteration
+ {
+ get { return m_sharedParallelStateFlags.ShouldExitLoop(CurrentIteration); }
+ }
+
+ /// <summary>
+ /// Returns the lowest iteration at which Break() has been called, or
+ /// null if Break() has not yet been called.
+ /// </summary>
+ internal override long? InternalLowestBreakIteration
+ {
+ get {return m_sharedParallelStateFlags.NullableLowestBreakIteration; }
+ }
+
+ /// <summary>
+ /// Communicates that parallel tasks should stop when they reach a specified iteration element.
+ /// (which is CurrentIteration of the caller).
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">Break() called after Stop().</exception>
+ /// <remarks>
+ /// This is shared with all other concurrent threads in the system which are participating in the
+ /// loop's execution. After calling Break(), no additional iterations will be executed on
+ /// the current thread, and other worker threads will execute once they get beyond the calling iteration.
+ /// </remarks>
+ internal override void InternalBreak()
+ {
+ ParallelLoopState.Break(CurrentIteration, m_sharedParallelStateFlags);
+ }
+ }
+
+ /// <summary>
+ /// Allows independent iterations of a parallel loop to interact with other iterations.
+ /// </summary>
+ internal class ParallelLoopState64 : ParallelLoopState
+ {
+ private ParallelLoopStateFlags64 m_sharedParallelStateFlags;
+ private long m_currentIteration = 0;
+
+ /// <summary>
+ /// Internal constructor to ensure an instance isn't created by users.
+ /// </summary>
+ /// <param name="sharedParallelStateFlags">A flag shared among all threads participating
+ /// in the execution of a certain loop.</param>
+ internal ParallelLoopState64(ParallelLoopStateFlags64 sharedParallelStateFlags)
+ : base(sharedParallelStateFlags)
+ {
+ m_sharedParallelStateFlags = sharedParallelStateFlags;
+ }
+
+ /// <summary>
+ /// Tracks the current loop iteration for the owning task.
+ /// This is used to compute whether or not the task should
+ /// terminate early due to a Break() call.
+ /// </summary>
+ internal long CurrentIteration
+ {
+ // No interlocks needed, because this value is only accessed in a single thread.
+ get {return m_currentIteration;}
+ set {m_currentIteration = value; }
+ }
+
+ /// <summary>
+ /// Returns true if we should be exiting from the current iteration
+ /// due to Stop(), Break() or exception.
+ /// </summary>
+ internal override bool InternalShouldExitCurrentIteration
+ {
+ get { return m_sharedParallelStateFlags.ShouldExitLoop(CurrentIteration); }
+ }
+
+ /// <summary>
+ /// Returns the lowest iteration at which Break() has been called, or
+ /// null if Break() has not yet been called.
+ /// </summary>
+ internal override long? InternalLowestBreakIteration
+ {
+ // We don't need to worry about torn read/write here because
+ // ParallelStateFlags64.LowestBreakIteration property is protected
+ // by an Interlocked.Read().
+ get { return m_sharedParallelStateFlags.NullableLowestBreakIteration; }
+ }
+
+ /// <summary>
+ /// Communicates that parallel tasks should stop when they reach a specified iteration element.
+ /// (which is CurrentIteration of the caller).
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">Break() called after Stop().</exception>
+ /// <remarks>
+ /// Atomically sets shared StoppedBroken flag to BROKEN, then atomically sets shared
+ /// LowestBreakIteration to CurrentIteration, but only if CurrentIteration is less than
+ /// LowestBreakIteration.
+ /// </remarks>
+ internal override void InternalBreak()
+ {
+ ParallelLoopState.Break(CurrentIteration, m_sharedParallelStateFlags);
+ }
+
+ }
+
+ /// <summary>
+ /// State information that is common between ParallelStateFlags class
+ /// and ParallelStateFlags64 class.
+ /// </summary>
+ internal class ParallelLoopStateFlags
+ {
+ internal static int PLS_NONE;
+ internal static int PLS_EXCEPTIONAL = 1;
+ internal static int PLS_BROKEN = 2;
+ internal static int PLS_STOPPED = 4;
+ internal static int PLS_CANCELED = 8;
+
+ private volatile int m_LoopStateFlags = PLS_NONE;
+
+ internal int LoopStateFlags
+ {
+ get { return m_LoopStateFlags; }
+ }
+
+ internal bool AtomicLoopStateUpdate(int newState, int illegalStates)
+ {
+ int oldState = 0;
+ return AtomicLoopStateUpdate(newState, illegalStates, ref oldState);
+ }
+
+ internal bool AtomicLoopStateUpdate(int newState, int illegalStates, ref int oldState)
+ {
+ SpinWait sw = new SpinWait();
+
+ do
+ {
+ oldState = m_LoopStateFlags;
+ if ((oldState & illegalStates) != 0) return false;
+ if (Interlocked.CompareExchange(ref m_LoopStateFlags, oldState | newState, oldState) == oldState)
+ {
+ return true;
+ }
+ sw.SpinOnce();
+ } while (true);
+
+ }
+
+ internal void SetExceptional()
+ {
+ // we can set the exceptional flag regardless of the state of other bits.
+ AtomicLoopStateUpdate(PLS_EXCEPTIONAL, PLS_NONE);
+ }
+
+ internal void Stop()
+ {
+ // disallow setting of PLS_STOPPED bit only if PLS_BROKEN was already set
+ if (!AtomicLoopStateUpdate(PLS_STOPPED, PLS_BROKEN))
+ {
+ throw new InvalidOperationException(
+ Environment.GetResourceString("ParallelState_Stop_InvalidOperationException_StopAfterBreak"));
+ }
+ }
+
+ // Returns true if StoppedBroken is updated to PLS_CANCELED.
+ internal bool Cancel()
+ {
+ // we can set the canceled flag regardless of the state of other bits.
+ return (AtomicLoopStateUpdate(PLS_CANCELED, PLS_NONE));
+ }
+ }
+
+ /// <summary>
+ /// An internal class used to share accounting information in 32-bit versions
+ /// of For()/ForEach() loops.
+ /// </summary>
+ internal class ParallelLoopStateFlags32 : ParallelLoopStateFlags
+ {
+ // Records the lowest iteration at which a Break() has been called,
+ // or Int32.MaxValue if no break has been called. Used directly
+ // by Break().
+ internal volatile int m_lowestBreakIteration = Int32.MaxValue;
+
+ // Not strictly necessary, but maintains consistency with ParallelStateFlags64
+ internal int LowestBreakIteration
+ {
+ get { return m_lowestBreakIteration; }
+ }
+
+ // Does some processing to convert m_lowestBreakIteration to a long?.
+ internal long? NullableLowestBreakIteration
+ {
+ get
+ {
+ if (m_lowestBreakIteration == Int32.MaxValue) return null;
+ else
+ {
+ // protect against torn read of 64-bit value
+ long rval = m_lowestBreakIteration;
+ if (IntPtr.Size >= 8) return rval;
+ else return Interlocked.Read(ref rval);
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Lets the caller know whether or not to prematurely exit the For/ForEach loop.
+ /// If this returns true, then exit the loop. Otherwise, keep going.
+ /// </summary>
+ /// <param name="CallerIteration">The caller's current iteration point
+ /// in the loop.</param>
+ /// <remarks>
+ /// The loop should exit on any one of the following conditions:
+ /// (1) Stop() has been called by one or more tasks.
+ /// (2) An exception has been raised by one or more tasks.
+ /// (3) Break() has been called by one or more tasks, and
+ /// CallerIteration exceeds the (lowest) iteration at which
+ /// Break() was called.
+ /// (4) The loop was canceled.
+ /// </remarks>
+ internal bool ShouldExitLoop(int CallerIteration)
+ {
+ int flags = LoopStateFlags;
+ return (flags != PLS_NONE && (
+ ((flags & (PLS_EXCEPTIONAL | PLS_STOPPED | PLS_CANCELED)) != 0) ||
+ (((flags & PLS_BROKEN) != 0) && (CallerIteration > LowestBreakIteration))));
+ }
+
+ // This lighter version of ShouldExitLoop will be used when the body type doesn't contain a state.
+ // Since simpler bodies cannot stop or break, we can safely skip checks for those flags here.
+ internal bool ShouldExitLoop()
+ {
+ int flags = LoopStateFlags;
+ return ((flags != PLS_NONE) && ((flags & (PLS_EXCEPTIONAL | PLS_CANCELED)) != 0));
+ }
+ }
+
+ /// <summary>
+ /// An internal class used to share accounting information in 64-bit versions
+ /// of For()/ForEach() loops.
+ /// </summary>
+ internal class ParallelLoopStateFlags64 : ParallelLoopStateFlags
+ {
+ // Records the lowest iteration at which a Break() has been called,
+ // or Int64.MaxValue if no break has been called. Used directly
+ // by Break().
+ internal long m_lowestBreakIteration = Int64.MaxValue;
+
+ // Performs a conditionally interlocked read of m_lowestBreakIteration.
+ internal long LowestBreakIteration
+ {
+ get
+ {
+ if (IntPtr.Size >= 8) return m_lowestBreakIteration;
+ else return Interlocked.Read(ref m_lowestBreakIteration);
+ }
+ }
+
+ // Does some processing to convert m_lowestBreakIteration to a long?.
+ internal long? NullableLowestBreakIteration
+ {
+ get
+ {
+ if (m_lowestBreakIteration == Int64.MaxValue) return null;
+ else
+ {
+ if (IntPtr.Size >= 8) return m_lowestBreakIteration;
+ else return Interlocked.Read(ref m_lowestBreakIteration);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Lets the caller know whether or not to prematurely exit the For/ForEach loop.
+ /// If this returns true, then exit the loop. Otherwise, keep going.
+ /// </summary>
+ /// <param name="CallerIteration">The caller's current iteration point
+ /// in the loop.</param>
+ /// <remarks>
+ /// The loop should exit on any one of the following conditions:
+ /// (1) Stop() has been called by one or more tasks.
+ /// (2) An exception has been raised by one or more tasks.
+ /// (3) Break() has been called by one or more tasks, and
+ /// CallerIteration exceeds the (lowest) iteration at which
+ /// Break() was called.
+ /// (4) The loop has been canceled.
+ /// </remarks>
+ internal bool ShouldExitLoop(long CallerIteration)
+ {
+ int flags = LoopStateFlags;
+ return (flags != PLS_NONE && (
+ ((flags & (PLS_EXCEPTIONAL | PLS_STOPPED | PLS_CANCELED)) != 0) ||
+ (((flags & PLS_BROKEN) != 0) && (CallerIteration > LowestBreakIteration))));
+ }
+
+ // This lighter version of ShouldExitLoop will be used when the body type doesn't contain a state.
+ // Since simpler bodies cannot stop or break, we can safely skip checks for those flags here.
+ internal bool ShouldExitLoop()
+ {
+ int flags = LoopStateFlags;
+ return ((flags != PLS_NONE) && ((flags & (PLS_EXCEPTIONAL | PLS_CANCELED)) != 0));
+ }
+ }
+
+ /// <summary>
+ /// Provides completion status on the execution of a <see cref="Parallel"/> loop.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="IsCompleted"/> returns true, then the loop ran to completion, such that all iterations
+ /// of the loop were executed. If <see cref="IsCompleted"/> returns false and <see
+ /// cref="LowestBreakIteration"/> returns null, a call to <see
+ /// cref="System.Threading.Tasks.ParallelLoopState.Stop"/> was used to end the loop prematurely. If <see
+ /// cref="IsCompleted"/> returns false and <see cref="LowestBreakIteration"/> returns a non-null integral
+ /// value, <see cref="System.Threading.Tasks.ParallelLoopState.Break()"/> was used to end the loop prematurely.
+ /// </remarks>
+ public struct ParallelLoopResult
+ {
+ internal bool m_completed;
+ internal long? m_lowestBreakIteration;
+
+ /// <summary>
+ /// Gets whether the loop ran to completion, such that all iterations of the loop were executed
+ /// and the loop didn't receive a request to end prematurely.
+ /// </summary>
+ public bool IsCompleted { get { return m_completed; } }
+
+ /// <summary>
+ /// Gets the index of the lowest iteration from which <see
+ /// cref="System.Threading.Tasks.ParallelLoopState.Break()"/>
+ /// was called.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="System.Threading.Tasks.ParallelLoopState.Break()"/> was not employed, this property will
+ /// return null.
+ /// </remarks>
+ public long? LowestBreakIteration { get { return m_lowestBreakIteration; } }
+ }
+
+}
+
+#pragma warning restore 0420
diff --git a/src/mscorlib/src/System/Threading/Tasks/ParallelRangeManager.cs b/src/mscorlib/src/System/Threading/Tasks/ParallelRangeManager.cs
new file mode 100644
index 0000000000..c4b66c41a9
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/ParallelRangeManager.cs
@@ -0,0 +1,278 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Implements the algorithm for distributing loop indices to parallel loop workers
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Threading;
+using System.Diagnostics.Contracts;
+
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Represents an index range
+ /// </summary>
+ internal struct IndexRange
+ {
+ // the From and To values for this range. These do not change.
+ internal long m_nFromInclusive;
+ internal long m_nToExclusive;
+
+ // The shared index, stored as the offset from nFromInclusive. Using an offset rather than the actual
+ // value saves us from overflows that can happen due to multiple workers racing to increment this.
+ // All updates to this field need to be interlocked.
+ internal volatile Shared<long> m_nSharedCurrentIndexOffset;
+
+ // to be set to 1 by the worker that finishes this range. It's OK to do a non-interlocked write here.
+ internal int m_bRangeFinished;
+ }
+
+
+ /// <summary>
+ /// The RangeWorker struct wraps the state needed by a task that services the parallel loop
+ /// </summary>
+ internal struct RangeWorker
+ {
+ // reference to the IndexRange array allocated by the range manager
+ internal readonly IndexRange[] m_indexRanges;
+
+ // index of the current index range that this worker is grabbing chunks from
+ internal int m_nCurrentIndexRange;
+
+ // the step for this loop. Duplicated here for quick access (rather than jumping to rangemanager)
+ internal long m_nStep;
+
+ // increment value is the current amount that this worker will use
+ // to increment the shared index of the range it's working on
+ internal long m_nIncrementValue;
+
+ // the increment value is doubled each time this worker finds work, and is capped at this value
+ internal readonly long m_nMaxIncrementValue;
+
+ /// <summary>
+ /// Initializes a RangeWorker struct
+ /// </summary>
+ internal RangeWorker(IndexRange[] ranges, int nInitialRange, long nStep)
+ {
+ m_indexRanges = ranges;
+ m_nCurrentIndexRange = nInitialRange;
+ m_nStep = nStep;
+
+ m_nIncrementValue = nStep;
+
+ m_nMaxIncrementValue = Parallel.DEFAULT_LOOP_STRIDE * nStep;
+ }
+
+ /// <summary>
+ /// Implements the core work search algorithm that will be used for this range worker.
+ /// </summary>
+ ///
+ /// Usage pattern is:
+ /// 1) the thread associated with this rangeworker calls FindNewWork
+ /// 2) if we return true, the worker uses the nFromInclusiveLocal and nToExclusiveLocal values
+ /// to execute the sequential loop
+ /// 3) if we return false it means there is no more work left. It's time to quit.
+ ///
+ internal bool FindNewWork(out long nFromInclusiveLocal, out long nToExclusiveLocal)
+ {
+ // since we iterate over index ranges circularly, we will use the
+ // count of visited ranges as our exit condition
+ int numIndexRangesToVisit = m_indexRanges.Length;
+
+ do
+ {
+ // local snap to save array access bounds checks in places where we only read fields
+ IndexRange currentRange = m_indexRanges[m_nCurrentIndexRange];
+
+ if (currentRange.m_bRangeFinished == 0)
+ {
+ if (m_indexRanges[m_nCurrentIndexRange].m_nSharedCurrentIndexOffset == null)
+ {
+ Interlocked.CompareExchange(ref m_indexRanges[m_nCurrentIndexRange].m_nSharedCurrentIndexOffset, new Shared<long>(0), null);
+ }
+
+ // this access needs to be on the array slot
+ long nMyOffset = Interlocked.Add(ref m_indexRanges[m_nCurrentIndexRange].m_nSharedCurrentIndexOffset.Value,
+ m_nIncrementValue) - m_nIncrementValue;
+
+ if (currentRange.m_nToExclusive - currentRange.m_nFromInclusive > nMyOffset)
+ {
+ // we found work
+
+ nFromInclusiveLocal = currentRange.m_nFromInclusive + nMyOffset;
+ nToExclusiveLocal = nFromInclusiveLocal + m_nIncrementValue;
+
+ // Check for going past end of range, or wrapping
+ if ( (nToExclusiveLocal > currentRange.m_nToExclusive) || (nToExclusiveLocal < currentRange.m_nFromInclusive) )
+ {
+ nToExclusiveLocal = currentRange.m_nToExclusive;
+ }
+
+ // We will double our unit of increment until it reaches the maximum.
+ if (m_nIncrementValue < m_nMaxIncrementValue)
+ {
+ m_nIncrementValue *= 2;
+ if (m_nIncrementValue > m_nMaxIncrementValue)
+ {
+ m_nIncrementValue = m_nMaxIncrementValue;
+ }
+ }
+
+ return true;
+ }
+ else
+ {
+ // this index range is completed, mark it so that others can skip it quickly
+ Interlocked.Exchange(ref m_indexRanges[m_nCurrentIndexRange].m_bRangeFinished, 1);
+ }
+ }
+
+ // move on to the next index range, in circular order.
+ m_nCurrentIndexRange = (m_nCurrentIndexRange + 1) % m_indexRanges.Length;
+ numIndexRangesToVisit--;
+
+ } while (numIndexRangesToVisit > 0);
+ // we've visited all index ranges possible => there's no work remaining
+
+ nFromInclusiveLocal = 0;
+ nToExclusiveLocal = 0;
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// 32 bit integer version of FindNewWork. Assumes the ranges were initialized with 32 bit values.
+ /// </summary>
+ internal bool FindNewWork32(out int nFromInclusiveLocal32, out int nToExclusiveLocal32)
+ {
+ long nFromInclusiveLocal;
+ long nToExclusiveLocal;
+
+ bool bRetVal = FindNewWork(out nFromInclusiveLocal, out nToExclusiveLocal);
+
+ Contract.Assert((nFromInclusiveLocal <= Int32.MaxValue) && (nFromInclusiveLocal >= Int32.MinValue) &&
+ (nToExclusiveLocal <= Int32.MaxValue) && (nToExclusiveLocal >= Int32.MinValue));
+
+ // convert to 32 bit before returning
+ nFromInclusiveLocal32 = (int)nFromInclusiveLocal;
+ nToExclusiveLocal32 = (int)nToExclusiveLocal;
+
+ return bRetVal;
+ }
+ }
+
+
+ /// <summary>
+ /// Represents the entire loop operation, keeping track of workers and ranges.
+ /// </summary>
+ ///
+ /// The usage pattern is:
+ /// 1) The Parallel loop entry function (ForWorker) creates an instance of this class
+ /// 2) Every thread joining to service the parallel loop calls RegisterWorker to grab a
+ /// RangeWorker struct to wrap the state it will need to find and execute work,
+ /// and they keep interacting with that struct until the end of the loop
+ internal class RangeManager
+ {
+ internal readonly IndexRange[] m_indexRanges;
+
+ internal int m_nCurrentIndexRangeToAssign;
+ internal long m_nStep;
+
+ /// <summary>
+ /// Initializes a RangeManager with the given loop parameters, and the desired number of outer ranges
+ /// </summary>
+ internal RangeManager(long nFromInclusive, long nToExclusive, long nStep, int nNumExpectedWorkers)
+ {
+ m_nCurrentIndexRangeToAssign = 0;
+ m_nStep = nStep;
+
+ // Our signed math breaks down w/ nNumExpectedWorkers == 1. So change it to 2.
+ if (nNumExpectedWorkers == 1)
+ nNumExpectedWorkers = 2;
+
+ //
+ // calculate the size of each index range
+ //
+
+ ulong uSpan = (ulong)(nToExclusive - nFromInclusive);
+ ulong uRangeSize = uSpan / (ulong) nNumExpectedWorkers; // rough estimate first
+
+ uRangeSize -= uRangeSize % (ulong) nStep; // snap to multiples of nStep
+ // otherwise index range transitions will derail us from nStep
+
+ if (uRangeSize == 0)
+ {
+ uRangeSize = (ulong) nStep;
+ }
+
+ //
+ // find the actual number of index ranges we will need
+ //
+ Contract.Assert((uSpan / uRangeSize) < Int32.MaxValue);
+
+ int nNumRanges = (int)(uSpan / uRangeSize);
+
+ if (uSpan % uRangeSize != 0)
+ {
+ nNumRanges++;
+ }
+
+
+ // Convert to signed so the rest of the logic works.
+ // Should be fine so long as uRangeSize < Int64.MaxValue, which we guaranteed by setting #workers >= 2.
+ long nRangeSize = (long)uRangeSize;
+
+ // allocate the array of index ranges
+ m_indexRanges = new IndexRange[nNumRanges];
+
+ long nCurrentIndex = nFromInclusive;
+ for (int i = 0; i < nNumRanges; i++)
+ {
+ // the fromInclusive of the new index range is always on nCurrentIndex
+ m_indexRanges[i].m_nFromInclusive = nCurrentIndex;
+ m_indexRanges[i].m_nSharedCurrentIndexOffset = null;
+ m_indexRanges[i].m_bRangeFinished = 0;
+
+ // now increment it to find the toExclusive value for our range
+ nCurrentIndex += nRangeSize;
+
+ // detect integer overflow or range overage and snap to nToExclusive
+ if (nCurrentIndex < nCurrentIndex - nRangeSize ||
+ nCurrentIndex > nToExclusive)
+ {
+ // this should only happen at the last index
+ Contract.Assert(i == nNumRanges - 1);
+
+ nCurrentIndex = nToExclusive;
+ }
+
+ // now that the end point of the new range is calculated, assign it.
+ m_indexRanges[i].m_nToExclusive = nCurrentIndex;
+ }
+ }
+
+ /// <summary>
+ /// The function that needs to be called by each new worker thread servicing the parallel loop
+ /// in order to get a RangeWorker struct that wraps the state for finding and executing indices
+ /// </summary>
+ internal RangeWorker RegisterNewWorker()
+ {
+ Contract.Assert(m_indexRanges != null && m_indexRanges.Length != 0);
+
+ int nInitialRange = (Interlocked.Increment(ref m_nCurrentIndexRangeToAssign) - 1) % m_indexRanges.Length;
+
+ return new RangeWorker(m_indexRanges, nInitialRange, m_nStep);
+ }
+ }
+}
+#pragma warning restore 0420
diff --git a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs
new file mode 100644
index 0000000000..462ee0a9bd
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs
@@ -0,0 +1,553 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Specialized producer/consumer queues.
+//
+//
+// ************<IMPORTANT NOTE>*************
+//
+// src\ndp\clr\src\bcl\system\threading\tasks\producerConsumerQueue.cs
+// src\ndp\fx\src\dataflow\system\threading\tasks\dataflow\internal\producerConsumerQueue.cs
+// Keep both of them consistent by changing the other file when you change this one, also avoid:
+// 1- To reference interneal types in mscorlib
+// 2- To reference any dataflow specific types
+// This should be fixed post Dev11 when this class becomes public.
+//
+// ************</IMPORTANT NOTE>*************
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>Represents a producer/consumer queue used internally by dataflow blocks.</summary>
+ /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+ internal interface IProducerConsumerQueue<T> : IEnumerable<T>
+ {
+ /// <summary>Enqueues an item into the queue.</summary>
+ /// <param name="item">The item to enqueue.</param>
+ /// <remarks>This method is meant to be thread-safe subject to the particular nature of the implementation.</remarks>
+ void Enqueue(T item);
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ /// <remarks>This method is meant to be thread-safe subject to the particular nature of the implementation.</remarks>
+ bool TryDequeue(out T result);
+
+ /// <summary>Gets whether the collection is currently empty.</summary>
+ /// <remarks>This method may or may not be thread-safe.</remarks>
+ bool IsEmpty { get; }
+
+ /// <summary>Gets the number of items in the collection.</summary>
+ /// <remarks>In many implementations, this method will not be thread-safe.</remarks>
+ int Count { get; }
+
+ /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+ /// <param name="syncObj">The sync object used to lock</param>
+ /// <returns>The collection count</returns>
+ int GetCountSafe(object syncObj);
+ }
+
+ /// <summary>
+ /// Provides a producer/consumer queue safe to be used by any number of producers and consumers concurrently.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+ [DebuggerDisplay("Count = {Count}")]
+ internal sealed class MultiProducerMultiConsumerQueue<T> : ConcurrentQueue<T>, IProducerConsumerQueue<T>
+ {
+ /// <summary>Enqueues an item into the queue.</summary>
+ /// <param name="item">The item to enqueue.</param>
+ void IProducerConsumerQueue<T>.Enqueue(T item) { base.Enqueue(item); }
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ bool IProducerConsumerQueue<T>.TryDequeue(out T result) { return base.TryDequeue(out result); }
+
+ /// <summary>Gets whether the collection is currently empty.</summary>
+ bool IProducerConsumerQueue<T>.IsEmpty { get { return base.IsEmpty; } }
+
+ /// <summary>Gets the number of items in the collection.</summary>
+ int IProducerConsumerQueue<T>.Count { get { return base.Count; } }
+
+ /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+ /// <remarks>ConcurrentQueue.Count is thread safe, no need to acquire the lock.</remarks>
+ int IProducerConsumerQueue<T>.GetCountSafe(object syncObj) { return base.Count; }
+ }
+
+ /// <summary>
+ /// Provides a producer/consumer queue safe to be used by only one producer and one consumer concurrently.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(SingleProducerSingleConsumerQueue<>.SingleProducerSingleConsumerQueue_DebugView))]
+ internal sealed class SingleProducerSingleConsumerQueue<T> : IProducerConsumerQueue<T>
+ {
+ // Design:
+ //
+ // SingleProducerSingleConsumerQueue (SPSCQueue) is a concurrent queue designed to be used
+ // by one producer thread and one consumer thread. SPSCQueue does not work correctly when used by
+ // multiple producer threads concurrently or multiple consumer threads concurrently.
+ //
+ // SPSCQueue is based on segments that behave like circular buffers. Each circular buffer is represented
+ // as an array with two indexes: m_first and m_last. m_first is the index of the array slot for the consumer
+ // to read next, and m_last is the slot for the producer to write next. The circular buffer is empty when
+ // (m_first == m_last), and full when ((m_last+1) % m_array.Length == m_first).
+ //
+ // Since m_first is only ever modified by the consumer thread and m_last by the producer, the two indices can
+ // be updated without interlocked operations. As long as the queue size fits inside a single circular buffer,
+ // enqueues and dequeues simply advance the corresponding indices around the circular buffer. If an enqueue finds
+ // that there is no room in the existing buffer, however, a new circular buffer is allocated that is twice as big
+ // as the old buffer. From then on, the producer will insert values into the new buffer. The consumer will first
+ // empty out the old buffer and only then follow the producer into the new (larger) buffer.
+ //
+ // As described above, the enqueue operation on the fast path only modifies the m_first field of the current segment.
+ // However, it also needs to read m_last in order to verify that there is room in the current segment. Similarly, the
+ // dequeue operation on the fast path only needs to modify m_last, but also needs to read m_first to verify that the
+ // queue is non-empty. This results in true cache line sharing between the producer and the consumer.
+ //
+ // The cache line sharing issue can be mitigating by having a possibly stale copy of m_first that is owned by the producer,
+ // and a possibly stale copy of m_last that is owned by the consumer. So, the consumer state is described using
+ // (m_first, m_lastCopy) and the producer state using (m_firstCopy, m_last). The consumer state is separated from
+ // the producer state by padding, which allows fast-path enqueues and dequeues from hitting shared cache lines.
+ // m_lastCopy is the consumer's copy of m_last. Whenever the consumer can tell that there is room in the buffer
+ // simply by observing m_lastCopy, the consumer thread does not need to read m_last and thus encounter a cache miss. Only
+ // when the buffer appears to be empty will the consumer refresh m_lastCopy from m_last. m_firstCopy is used by the producer
+ // in the same way to avoid reading m_first on the hot path.
+
+ /// <summary>The initial size to use for segments (in number of elements).</summary>
+ private const int INIT_SEGMENT_SIZE = 32; // must be a power of 2
+ /// <summary>The maximum size to use for segments (in number of elements).</summary>
+ private const int MAX_SEGMENT_SIZE = 0x1000000; // this could be made as large as Int32.MaxValue / 2
+
+ /// <summary>The head of the linked list of segments.</summary>
+ private volatile Segment m_head;
+ /// <summary>The tail of the linked list of segments.</summary>
+ private volatile Segment m_tail;
+
+ /// <summary>Initializes the queue.</summary>
+ internal SingleProducerSingleConsumerQueue()
+ {
+ // Validate constants in ctor rather than in an explicit cctor that would cause perf degradation
+ Contract.Assert(INIT_SEGMENT_SIZE > 0, "Initial segment size must be > 0.");
+ Contract.Assert((INIT_SEGMENT_SIZE & (INIT_SEGMENT_SIZE - 1)) == 0, "Initial segment size must be a power of 2");
+ Contract.Assert(INIT_SEGMENT_SIZE <= MAX_SEGMENT_SIZE, "Initial segment size should be <= maximum.");
+ Contract.Assert(MAX_SEGMENT_SIZE < Int32.MaxValue / 2, "Max segment size * 2 must be < Int32.MaxValue, or else overflow could occur.");
+
+ // Initialize the queue
+ m_head = m_tail = new Segment(INIT_SEGMENT_SIZE);
+ }
+
+ /// <summary>Enqueues an item into the queue.</summary>
+ /// <param name="item">The item to enqueue.</param>
+ public void Enqueue(T item)
+ {
+ Segment segment = m_tail;
+ var array = segment.m_array;
+ int last = segment.m_state.m_last; // local copy to avoid multiple volatile reads
+
+ // Fast path: there's obviously room in the current segment
+ int tail2 = (last + 1) & (array.Length - 1);
+ if (tail2 != segment.m_state.m_firstCopy)
+ {
+ array[last] = item;
+ segment.m_state.m_last = tail2;
+ }
+ // Slow path: there may not be room in the current segment.
+ else EnqueueSlow(item, ref segment);
+ }
+
+ /// <summary>Enqueues an item into the queue.</summary>
+ /// <param name="item">The item to enqueue.</param>
+ /// <param name="segment">The segment in which to first attempt to store the item.</param>
+ private void EnqueueSlow(T item, ref Segment segment)
+ {
+ Contract.Requires(segment != null, "Expected a non-null segment.");
+
+ if (segment.m_state.m_firstCopy != segment.m_state.m_first)
+ {
+ segment.m_state.m_firstCopy = segment.m_state.m_first;
+ Enqueue(item); // will only recur once for this enqueue operation
+ return;
+ }
+
+ int newSegmentSize = m_tail.m_array.Length << 1; // double size
+ Contract.Assert(newSegmentSize > 0, "The max size should always be small enough that we don't overflow.");
+ if (newSegmentSize > MAX_SEGMENT_SIZE) newSegmentSize = MAX_SEGMENT_SIZE;
+
+ var newSegment = new Segment(newSegmentSize);
+ newSegment.m_array[0] = item;
+ newSegment.m_state.m_last = 1;
+ newSegment.m_state.m_lastCopy = 1;
+
+ try { } finally
+ {
+ // Finally block to protect against corruption due to a thread abort
+ // between setting m_next and setting m_tail.
+ Volatile.Write(ref m_tail.m_next, newSegment); // ensure segment not published until item is fully stored
+ m_tail = newSegment;
+ }
+ }
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ public bool TryDequeue(out T result)
+ {
+ Segment segment = m_head;
+ var array = segment.m_array;
+ int first = segment.m_state.m_first; // local copy to avoid multiple volatile reads
+
+ // Fast path: there's obviously data available in the current segment
+ if (first != segment.m_state.m_lastCopy)
+ {
+ result = array[first];
+ array[first] = default(T); // Clear the slot to release the element
+ segment.m_state.m_first = (first + 1) & (array.Length - 1);
+ return true;
+ }
+ // Slow path: there may not be data available in the current segment
+ else return TryDequeueSlow(ref segment, ref array, out result);
+ }
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="array">The array from which the item was dequeued.</param>
+ /// <param name="segment">The segment from which the item was dequeued.</param>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ private bool TryDequeueSlow(ref Segment segment, ref T[] array, out T result)
+ {
+ Contract.Requires(segment != null, "Expected a non-null segment.");
+ Contract.Requires(array != null, "Expected a non-null item array.");
+
+ if (segment.m_state.m_last != segment.m_state.m_lastCopy)
+ {
+ segment.m_state.m_lastCopy = segment.m_state.m_last;
+ return TryDequeue(out result); // will only recur once for this dequeue operation
+ }
+
+ if (segment.m_next != null && segment.m_state.m_first == segment.m_state.m_last)
+ {
+ segment = segment.m_next;
+ array = segment.m_array;
+ m_head = segment;
+ }
+
+ var first = segment.m_state.m_first; // local copy to avoid extraneous volatile reads
+
+ if (first == segment.m_state.m_last)
+ {
+ result = default(T);
+ return false;
+ }
+
+ result = array[first];
+ array[first] = default(T); // Clear the slot to release the element
+ segment.m_state.m_first = (first + 1) & (segment.m_array.Length - 1);
+ segment.m_state.m_lastCopy = segment.m_state.m_last; // Refresh m_lastCopy to ensure that m_first has not passed m_lastCopy
+
+ return true;
+ }
+
+ /// <summary>Attempts to peek at an item in the queue.</summary>
+ /// <param name="result">The peeked item.</param>
+ /// <returns>true if an item could be peeked; otherwise, false.</returns>
+ public bool TryPeek(out T result)
+ {
+ Segment segment = m_head;
+ var array = segment.m_array;
+ int first = segment.m_state.m_first; // local copy to avoid multiple volatile reads
+
+ // Fast path: there's obviously data available in the current segment
+ if (first != segment.m_state.m_lastCopy)
+ {
+ result = array[first];
+ return true;
+ }
+ // Slow path: there may not be data available in the current segment
+ else return TryPeekSlow(ref segment, ref array, out result);
+ }
+
+ /// <summary>Attempts to peek at an item in the queue.</summary>
+ /// <param name="array">The array from which the item is peeked.</param>
+ /// <param name="segment">The segment from which the item is peeked.</param>
+ /// <param name="result">The peeked item.</param>
+ /// <returns>true if an item could be peeked; otherwise, false.</returns>
+ private bool TryPeekSlow(ref Segment segment, ref T[] array, out T result)
+ {
+ Contract.Requires(segment != null, "Expected a non-null segment.");
+ Contract.Requires(array != null, "Expected a non-null item array.");
+
+ if (segment.m_state.m_last != segment.m_state.m_lastCopy)
+ {
+ segment.m_state.m_lastCopy = segment.m_state.m_last;
+ return TryPeek(out result); // will only recur once for this peek operation
+ }
+
+ if (segment.m_next != null && segment.m_state.m_first == segment.m_state.m_last)
+ {
+ segment = segment.m_next;
+ array = segment.m_array;
+ m_head = segment;
+ }
+
+ var first = segment.m_state.m_first; // local copy to avoid extraneous volatile reads
+
+ if (first == segment.m_state.m_last)
+ {
+ result = default(T);
+ return false;
+ }
+
+ result = array[first];
+ return true;
+ }
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="predicate">The predicate that must return true for the item to be dequeued. If null, all items implicitly return true.</param>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ public bool TryDequeueIf(Predicate<T> predicate, out T result)
+ {
+ Segment segment = m_head;
+ var array = segment.m_array;
+ int first = segment.m_state.m_first; // local copy to avoid multiple volatile reads
+
+ // Fast path: there's obviously data available in the current segment
+ if (first != segment.m_state.m_lastCopy)
+ {
+ result = array[first];
+ if (predicate == null || predicate(result))
+ {
+ array[first] = default(T); // Clear the slot to release the element
+ segment.m_state.m_first = (first + 1) & (array.Length - 1);
+ return true;
+ }
+ else
+ {
+ result = default(T);
+ return false;
+ }
+ }
+ // Slow path: there may not be data available in the current segment
+ else return TryDequeueIfSlow(predicate, ref segment, ref array, out result);
+ }
+
+ /// <summary>Attempts to dequeue an item from the queue.</summary>
+ /// <param name="predicate">The predicate that must return true for the item to be dequeued. If null, all items implicitly return true.</param>
+ /// <param name="array">The array from which the item was dequeued.</param>
+ /// <param name="segment">The segment from which the item was dequeued.</param>
+ /// <param name="result">The dequeued item.</param>
+ /// <returns>true if an item could be dequeued; otherwise, false.</returns>
+ private bool TryDequeueIfSlow(Predicate<T> predicate, ref Segment segment, ref T[] array, out T result)
+ {
+ Contract.Requires(segment != null, "Expected a non-null segment.");
+ Contract.Requires(array != null, "Expected a non-null item array.");
+
+ if (segment.m_state.m_last != segment.m_state.m_lastCopy)
+ {
+ segment.m_state.m_lastCopy = segment.m_state.m_last;
+ return TryDequeueIf(predicate, out result); // will only recur once for this dequeue operation
+ }
+
+ if (segment.m_next != null && segment.m_state.m_first == segment.m_state.m_last)
+ {
+ segment = segment.m_next;
+ array = segment.m_array;
+ m_head = segment;
+ }
+
+ var first = segment.m_state.m_first; // local copy to avoid extraneous volatile reads
+
+ if (first == segment.m_state.m_last)
+ {
+ result = default(T);
+ return false;
+ }
+
+ result = array[first];
+ if (predicate == null || predicate(result))
+ {
+ array[first] = default(T); // Clear the slot to release the element
+ segment.m_state.m_first = (first + 1) & (segment.m_array.Length - 1);
+ segment.m_state.m_lastCopy = segment.m_state.m_last; // Refresh m_lastCopy to ensure that m_first has not passed m_lastCopy
+ return true;
+ }
+ else
+ {
+ result = default(T);
+ return false;
+ }
+ }
+
+ public void Clear()
+ {
+ T ignored;
+ while (TryDequeue(out ignored)) ;
+ }
+
+ /// <summary>Gets whether the collection is currently empty.</summary>
+ /// <remarks>WARNING: This should not be used concurrently without further vetting.</remarks>
+ public bool IsEmpty
+ {
+ // This implementation is optimized for calls from the consumer.
+ get
+ {
+ var head = m_head;
+ if (head.m_state.m_first != head.m_state.m_lastCopy) return false; // m_first is volatile, so the read of m_lastCopy cannot get reordered
+ if (head.m_state.m_first != head.m_state.m_last) return false;
+ return head.m_next == null;
+ }
+ }
+
+ /// <summary>Gets an enumerable for the collection.</summary>
+ /// <remarks>WARNING: This should only be used for debugging purposes. It is not safe to be used concurrently.</remarks>
+ public IEnumerator<T> GetEnumerator()
+ {
+ for (Segment segment = m_head; segment != null; segment = segment.m_next)
+ {
+ for (int pt = segment.m_state.m_first;
+ pt != segment.m_state.m_last;
+ pt = (pt + 1) & (segment.m_array.Length - 1))
+ {
+ yield return segment.m_array[pt];
+ }
+ }
+ }
+ /// <summary>Gets an enumerable for the collection.</summary>
+ /// <remarks>WARNING: This should only be used for debugging purposes. It is not safe to be used concurrently.</remarks>
+ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
+
+ /// <summary>Gets the number of items in the collection.</summary>
+ /// <remarks>WARNING: This should only be used for debugging purposes. It is not meant to be used concurrently.</remarks>
+ public int Count
+ {
+ get
+ {
+ int count = 0;
+ for (Segment segment = m_head; segment != null; segment = segment.m_next)
+ {
+ int arraySize = segment.m_array.Length;
+ int first, last;
+ while (true) // Count is not meant to be used concurrently, but this helps to avoid issues if it is
+ {
+ first = segment.m_state.m_first;
+ last = segment.m_state.m_last;
+ if (first == segment.m_state.m_first) break;
+ }
+ count += (last - first) & (arraySize - 1);
+ }
+ return count;
+ }
+ }
+
+ /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+ /// <remarks>The Count is not thread safe, so we need to acquire the lock.</remarks>
+ int IProducerConsumerQueue<T>.GetCountSafe(object syncObj)
+ {
+ Contract.Assert(syncObj != null, "The syncObj parameter is null.");
+ lock (syncObj)
+ {
+ return Count;
+ }
+ }
+
+ /// <summary>A segment in the queue containing one or more items.</summary>
+ [StructLayout(LayoutKind.Sequential)]
+ private sealed class Segment
+ {
+ /// <summary>The next segment in the linked list of segments.</summary>
+ internal Segment m_next;
+ /// <summary>The data stored in this segment.</summary>
+ internal readonly T[] m_array;
+ /// <summary>Details about the segment.</summary>
+ internal SegmentState m_state; // separated out to enable StructLayout attribute to take effect
+
+ /// <summary>Initializes the segment.</summary>
+ /// <param name="size">The size to use for this segment.</param>
+ internal Segment(int size)
+ {
+ Contract.Requires((size & (size - 1)) == 0, "Size must be a power of 2");
+ m_array = new T[size];
+ }
+ }
+
+ /// <summary>Stores information about a segment.</summary>
+ [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
+ private struct SegmentState
+ {
+ /// <summary>Padding to reduce false sharing between the segment's array and m_first.</summary>
+ internal PaddingFor32 m_pad0;
+
+ /// <summary>The index of the current head in the segment.</summary>
+ internal volatile int m_first;
+ /// <summary>A copy of the current tail index.</summary>
+ internal int m_lastCopy; // not volatile as read and written by the producer, except for IsEmpty, and there m_lastCopy is only read after reading the volatile m_first
+
+ /// <summary>Padding to reduce false sharing between the first and last.</summary>
+ internal PaddingFor32 m_pad1;
+
+ /// <summary>A copy of the current head index.</summary>
+ internal int m_firstCopy; // not voliatle as only read and written by the consumer thread
+ /// <summary>The index of the current tail in the segment.</summary>
+ internal volatile int m_last;
+
+ /// <summary>Padding to reduce false sharing with the last and what's after the segment.</summary>
+ internal PaddingFor32 m_pad2;
+ }
+
+ /// <summary>Debugger type proxy for a SingleProducerSingleConsumerQueue of T.</summary>
+ private sealed class SingleProducerSingleConsumerQueue_DebugView
+ {
+ /// <summary>The queue being visualized.</summary>
+ private readonly SingleProducerSingleConsumerQueue<T> m_queue;
+
+ /// <summary>Initializes the debug view.</summary>
+ /// <param name="enumerable">The queue being debugged.</param>
+ public SingleProducerSingleConsumerQueue_DebugView(SingleProducerSingleConsumerQueue<T> queue)
+ {
+ Contract.Requires(queue != null, "Expected a non-null queue.");
+ m_queue = queue;
+ }
+
+ /// <summary>Gets the contents of the list.</summary>
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items
+ {
+ get
+ {
+ List<T> list = new List<T>();
+ foreach (T item in m_queue)
+ list.Add(item);
+ return list.ToArray();
+ }
+ }
+ }
+ }
+
+
+ /// <summary>A placeholder class for common padding constants and eventually routines.</summary>
+ static class PaddingHelpers
+ {
+ /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary>
+ internal const int CACHE_LINE_SIZE = 128;
+ }
+
+ /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary>
+ [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines
+ struct PaddingFor32
+ {
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs
new file mode 100644
index 0000000000..5f79f30b35
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs
@@ -0,0 +1,763 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// EventSource for TPL.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.CompilerServices;
+
+namespace System.Threading.Tasks
+{
+ using System.Diagnostics.Tracing;
+
+ /// <summary>Provides an event source for tracing TPL information.</summary>
+ [EventSource(
+ Name = "System.Threading.Tasks.TplEventSource",
+ Guid = "2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5",
+ LocalizationResources = System.CoreLib.Name)]
+ internal sealed class TplEtwProvider : EventSource
+ {
+ /// Used to determine if tasks should generate Activity IDs for themselves
+ internal bool TasksSetActivityIds; // This keyword is set
+ internal bool Debug;
+ private bool DebugActivityId;
+
+ /// <summary>
+ /// Get callbacks when the ETW sends us commands`
+ /// </summary>
+ protected override void OnEventCommand(EventCommandEventArgs command)
+ {
+ // To get the AsyncCausality events, we need to inform the AsyncCausalityTracer
+ if (command.Command == EventCommand.Enable)
+ AsyncCausalityTracer.EnableToETW(true);
+ else if (command.Command == EventCommand.Disable)
+ AsyncCausalityTracer.EnableToETW(false);
+
+ if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds))
+ ActivityTracker.Instance.Enable();
+ else
+ TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds);
+
+ Debug = IsEnabled(EventLevel.Informational, Keywords.Debug);
+ DebugActivityId = IsEnabled(EventLevel.Informational, Keywords.DebugActivityId);
+ }
+
+ /// <summary>
+ /// Defines the singleton instance for the TPL ETW provider.
+ /// The TPL Event provider GUID is {2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5}.
+ /// </summary>
+ public static TplEtwProvider Log = new TplEtwProvider();
+ /// <summary>Prevent external instantiation. All logging should go through the Log instance.</summary>
+ private TplEtwProvider() { }
+
+ /// <summary>Type of a fork/join operation.</summary>
+ public enum ForkJoinOperationType
+ {
+ /// <summary>Parallel.Invoke.</summary>
+ ParallelInvoke=1,
+ /// <summary>Parallel.For.</summary>
+ ParallelFor=2,
+ /// <summary>Parallel.ForEach.</summary>
+ ParallelForEach=3
+ }
+
+ /// <summary>Configured behavior of a task wait operation.</summary>
+ public enum TaskWaitBehavior : int
+ {
+ /// <summary>A synchronous wait.</summary>
+ Synchronous = 1,
+ /// <summary>An asynchronous await.</summary>
+ Asynchronous = 2
+ }
+
+ /// <summary>ETW tasks that have start/stop events.</summary>
+ public class Tasks // this name is important for EventSource
+ {
+ /// <summary>A parallel loop.</summary>
+ public const EventTask Loop = (EventTask)1;
+ /// <summary>A parallel invoke.</summary>
+ public const EventTask Invoke = (EventTask)2;
+ /// <summary>Executing a Task.</summary>
+ public const EventTask TaskExecute = (EventTask)3;
+ /// <summary>Waiting on a Task.</summary>
+ public const EventTask TaskWait = (EventTask)4;
+ /// <summary>A fork/join task within a loop or invoke.</summary>
+ public const EventTask ForkJoin = (EventTask)5;
+ /// <summary>A task is scheduled to execute.</summary>
+ public const EventTask TaskScheduled = (EventTask)6;
+ /// <summary>An await task continuation is scheduled to execute.</summary>
+ public const EventTask AwaitTaskContinuationScheduled = (EventTask)7;
+
+ /// <summary>AsyncCausalityFunctionality.</summary>
+ public const EventTask TraceOperation = (EventTask)8;
+ public const EventTask TraceSynchronousWork = (EventTask)9;
+ }
+
+ public class Keywords // thisname is important for EventSource
+ {
+ /// <summary>
+ /// Only the most basic information about the workings of the task library
+ /// This sets activity IDS and logs when tasks are schedules (or waits begin)
+ /// But are otherwise silent
+ /// </summary>
+ public const EventKeywords TaskTransfer = (EventKeywords) 1;
+ /// <summary>
+ /// TaskTranser events plus events when tasks start and stop
+ /// </summary>
+ public const EventKeywords Tasks = (EventKeywords) 2;
+ /// <summary>
+ /// Events associted with the higher level parallel APIs
+ /// </summary>
+ public const EventKeywords Parallel = (EventKeywords) 4;
+ /// <summary>
+ /// These are relatively verbose events that effectively just redirect
+ /// the windows AsyncCausalityTracer to ETW
+ /// </summary>
+ public const EventKeywords AsyncCausalityOperation = (EventKeywords) 8;
+ public const EventKeywords AsyncCausalityRelation = (EventKeywords) 0x10;
+ public const EventKeywords AsyncCausalitySynchronousWork = (EventKeywords) 0x20;
+
+ /// <summary>
+ /// Emit the stops as well as the schedule/start events
+ /// </summary>
+ public const EventKeywords TaskStops = (EventKeywords) 0x40;
+
+ /// <summary>
+ /// TasksFlowActivityIds indicate that activity ID flow from one task
+ /// to any task created by it.
+ /// </summary>
+ public const EventKeywords TasksFlowActivityIds = (EventKeywords) 0x80;
+
+ /// <summary>
+ /// TasksSetActivityIds will cause the task operations to set Activity Ids
+ /// This option is incompatible with TasksFlowActivityIds flow is ignored
+ /// if that keyword is set. This option is likley to be removed in the future
+ /// </summary>
+ public const EventKeywords TasksSetActivityIds = (EventKeywords) 0x10000;
+
+ /// <summary>
+ /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future
+ /// </summary>
+ public const EventKeywords Debug = (EventKeywords) 0x20000;
+ /// <summary>
+ /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future
+ /// </summary>
+ public const EventKeywords DebugActivityId = (EventKeywords) 0x40000;
+ }
+
+ /// <summary>Enabled for all keywords.</summary>
+ private const EventKeywords ALL_KEYWORDS = (EventKeywords)(-1);
+
+ //-----------------------------------------------------------------------------------
+ //
+ // TPL Event IDs (must be unique)
+ //
+
+ /// <summary>The beginning of a parallel loop.</summary>
+ private const int PARALLELLOOPBEGIN_ID = 1;
+ /// <summary>The ending of a parallel loop.</summary>
+ private const int PARALLELLOOPEND_ID = 2;
+ /// <summary>The beginning of a parallel invoke.</summary>
+ private const int PARALLELINVOKEBEGIN_ID = 3;
+ /// <summary>The ending of a parallel invoke.</summary>
+ private const int PARALLELINVOKEEND_ID = 4;
+ /// <summary>A task entering a fork/join construct.</summary>
+ private const int PARALLELFORK_ID = 5;
+ /// <summary>A task leaving a fork/join construct.</summary>
+ private const int PARALLELJOIN_ID = 6;
+
+ /// <summary>A task is scheduled to a task scheduler.</summary>
+ private const int TASKSCHEDULED_ID = 7;
+ /// <summary>A task is about to execute.</summary>
+ private const int TASKSTARTED_ID = 8;
+ /// <summary>A task has finished executing.</summary>
+ private const int TASKCOMPLETED_ID = 9;
+ /// <summary>A wait on a task is beginning.</summary>
+ private const int TASKWAITBEGIN_ID = 10;
+ /// <summary>A wait on a task is ending.</summary>
+ private const int TASKWAITEND_ID = 11;
+ /// <summary>A continuation of a task is scheduled.</summary>
+ private const int AWAITTASKCONTINUATIONSCHEDULED_ID = 12;
+ /// <summary>A continuation of a taskWaitEnd is complete </summary>
+ private const int TASKWAITCONTINUATIONCOMPLETE_ID = 13;
+ /// <summary>A continuation of a taskWaitEnd is complete </summary>
+ private const int TASKWAITCONTINUATIONSTARTED_ID = 19;
+
+ private const int TRACEOPERATIONSTART_ID = 14;
+ private const int TRACEOPERATIONSTOP_ID = 15;
+ private const int TRACEOPERATIONRELATION_ID = 16;
+ private const int TRACESYNCHRONOUSWORKSTART_ID = 17;
+ private const int TRACESYNCHRONOUSWORKSTOP_ID = 18;
+
+
+ //-----------------------------------------------------------------------------------
+ //
+ // Parallel Events
+ //
+
+ #region ParallelLoopBegin
+ /// <summary>
+ /// Denotes the entry point for a Parallel.For or Parallel.ForEach loop
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The loop ID.</param>
+ /// <param name="OperationType">The kind of fork/join operation.</param>
+ /// <param name="InclusiveFrom">The lower bound of the loop.</param>
+ /// <param name="ExclusiveTo">The upper bound of the loop.</param>
+ [SecuritySafeCritical]
+ [Event(PARALLELLOOPBEGIN_ID, Level = EventLevel.Informational, ActivityOptions=EventActivityOptions.Recursive,
+ Task = TplEtwProvider.Tasks.Loop, Opcode = EventOpcode.Start)]
+ public void ParallelLoopBegin(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID, ForkJoinOperationType OperationType, // PFX_FORKJOIN_COMMON_EVENT_HEADER
+ long InclusiveFrom, long ExclusiveTo)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.Parallel))
+ {
+ // There is no explicit WriteEvent() overload matching this event's fields. Therefore calling
+ // WriteEvent() would hit the "params" overload, which leads to an object allocation every time
+ // this event is fired. To prevent that problem we will call WriteEventCore(), which works with
+ // a stack based EventData array populated with the event fields.
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[6];
+
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&ForkJoinContextID));
+ eventPayload[3].Size = sizeof(int);
+ eventPayload[3].DataPointer = ((IntPtr) (&OperationType));
+ eventPayload[4].Size = sizeof(long);
+ eventPayload[4].DataPointer = ((IntPtr) (&InclusiveFrom));
+ eventPayload[5].Size = sizeof(long);
+ eventPayload[5].DataPointer = ((IntPtr) (&ExclusiveTo));
+
+ WriteEventCore(PARALLELLOOPBEGIN_ID, 6, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ #region ParallelLoopEnd
+ /// <summary>
+ /// Denotes the end of a Parallel.For or Parallel.ForEach loop.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The loop ID.</param>
+ /// <param name="TotalIterations">the total number of iterations processed.</param>
+ [SecuritySafeCritical]
+ [Event(PARALLELLOOPEND_ID, Level = EventLevel.Informational, Task = TplEtwProvider.Tasks.Loop, Opcode = EventOpcode.Stop)]
+ public void ParallelLoopEnd(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID, long TotalIterations)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.Parallel))
+ {
+ // There is no explicit WriteEvent() overload matching this event's fields.
+ // Therefore calling WriteEvent() would hit the "params" overload, which leads to an object allocation every time this event is fired.
+ // To prevent that problem we will call WriteEventCore(), which works with a stack based EventData array populated with the event fields
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[4];
+
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&ForkJoinContextID));
+ eventPayload[3].Size = sizeof(long);
+ eventPayload[3].DataPointer = ((IntPtr) (&TotalIterations));
+
+ WriteEventCore(PARALLELLOOPEND_ID, 4, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ #region ParallelInvokeBegin
+ /// <summary>Denotes the entry point for a Parallel.Invoke call.</summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The invoke ID.</param>
+ /// <param name="OperationType">The kind of fork/join operation.</param>
+ /// <param name="ActionCount">The number of actions being invoked.</param>
+ [SecuritySafeCritical]
+ [Event(PARALLELINVOKEBEGIN_ID, Level = EventLevel.Informational, ActivityOptions=EventActivityOptions.Recursive,
+ Task = TplEtwProvider.Tasks.Invoke, Opcode = EventOpcode.Start)]
+ public void ParallelInvokeBegin(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID, ForkJoinOperationType OperationType, // PFX_FORKJOIN_COMMON_EVENT_HEADER
+ int ActionCount)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.Parallel))
+ {
+ // There is no explicit WriteEvent() overload matching this event's fields.
+ // Therefore calling WriteEvent() would hit the "params" overload, which leads to an object allocation every time this event is fired.
+ // To prevent that problem we will call WriteEventCore(), which works with a stack based EventData array populated with the event fields
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[5];
+
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&ForkJoinContextID));
+ eventPayload[3].Size = sizeof(int);
+ eventPayload[3].DataPointer = ((IntPtr) (&OperationType));
+ eventPayload[4].Size = sizeof(int);
+ eventPayload[4].DataPointer = ((IntPtr) (&ActionCount));
+
+ WriteEventCore(PARALLELINVOKEBEGIN_ID, 5, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ #region ParallelInvokeEnd
+ /// <summary>
+ /// Denotes the exit point for a Parallel.Invoke call.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The invoke ID.</param>
+ [Event(PARALLELINVOKEEND_ID, Level = EventLevel.Informational, Task = TplEtwProvider.Tasks.Invoke, Opcode = EventOpcode.Stop)]
+ public void ParallelInvokeEnd(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.Parallel))
+ {
+ WriteEvent(PARALLELINVOKEEND_ID, OriginatingTaskSchedulerID, OriginatingTaskID, ForkJoinContextID);
+ }
+ }
+ #endregion
+
+ #region ParallelFork
+ /// <summary>
+ /// Denotes the start of an individual task that's part of a fork/join context.
+ /// Before this event is fired, the start of the new fork/join context will be marked
+ /// with another event that declares a unique context ID.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The invoke ID.</param>
+ [Event(PARALLELFORK_ID, Level = EventLevel.Verbose, ActivityOptions=EventActivityOptions.Recursive,
+ Task = TplEtwProvider.Tasks.ForkJoin, Opcode = EventOpcode.Start)]
+ public void ParallelFork(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Verbose, Keywords.Parallel))
+ {
+ WriteEvent(PARALLELFORK_ID, OriginatingTaskSchedulerID, OriginatingTaskID, ForkJoinContextID);
+ }
+ }
+ #endregion
+
+ #region ParallelJoin
+ /// <summary>
+ /// Denotes the end of an individual task that's part of a fork/join context.
+ /// This should match a previous ParallelFork event with a matching "OriginatingTaskID"
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="ForkJoinContextID">The invoke ID.</param>
+ [Event(PARALLELJOIN_ID, Level = EventLevel.Verbose, Task = TplEtwProvider.Tasks.ForkJoin, Opcode = EventOpcode.Stop)]
+ public void ParallelJoin(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ForkJoinContextID)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Verbose, Keywords.Parallel))
+ {
+ WriteEvent(PARALLELJOIN_ID, OriginatingTaskSchedulerID, OriginatingTaskID, ForkJoinContextID);
+ }
+ }
+ #endregion
+
+ //-----------------------------------------------------------------------------------
+ //
+ // Task Events
+ //
+
+ // These are all verbose events, so we need to call IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)
+ // call. However since the IsEnabled(l,k) call is more expensive than IsEnabled(), we only want
+ // to incur this cost when instrumentation is enabled. So the Task codepaths that call these
+ // event functions still do the check for IsEnabled()
+
+ #region TaskScheduled
+ /// <summary>
+ /// Fired when a task is queued to a TaskScheduler.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ /// <param name="CreatingTaskID">The task ID</param>
+ /// <param name="TaskCreationOptions">The options used to create the task.</param>
+ [SecuritySafeCritical]
+ [Event(TASKSCHEDULED_ID, Task = Tasks.TaskScheduled, Version=1, Opcode = EventOpcode.Send,
+ Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)]
+ public void TaskScheduled(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int TaskID, int CreatingTaskID, int TaskCreationOptions, int appDomain)
+ {
+ // IsEnabled() call is an inlined quick check that makes this very fast when provider is off
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks))
+ {
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[6];
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&TaskID));
+ eventPayload[3].Size = sizeof(int);
+ eventPayload[3].DataPointer = ((IntPtr) (&CreatingTaskID));
+ eventPayload[4].Size = sizeof(int);
+ eventPayload[4].DataPointer = ((IntPtr) (&TaskCreationOptions));
+ eventPayload[5].Size = sizeof(int);
+ eventPayload[5].DataPointer = ((IntPtr) (&appDomain));
+ if (TasksSetActivityIds)
+ {
+ Guid childActivityId = CreateGuidForTaskID(TaskID);
+ WriteEventWithRelatedActivityIdCore(TASKSCHEDULED_ID, &childActivityId, 6, eventPayload);
+ }
+ else
+ WriteEventCore(TASKSCHEDULED_ID, 6, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ #region TaskStarted
+ /// <summary>
+ /// Fired just before a task actually starts executing.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ [Event(TASKSTARTED_ID,
+ Level = EventLevel.Informational, Keywords = Keywords.Tasks)]
+ public void TaskStarted(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int TaskID)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.Tasks))
+ WriteEvent(TASKSTARTED_ID, OriginatingTaskSchedulerID, OriginatingTaskID, TaskID);
+ }
+ #endregion
+
+ #region TaskCompleted
+ /// <summary>
+ /// Fired right after a task finished executing.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ /// <param name="IsExceptional">Whether the task completed due to an error.</param>
+ [SecuritySafeCritical]
+ [Event(TASKCOMPLETED_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.TaskStops)]
+ public void TaskCompleted(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int TaskID, bool IsExceptional)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.Tasks))
+ {
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[4];
+ Int32 isExceptionalInt = IsExceptional ? 1 : 0;
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&TaskID));
+ eventPayload[3].Size = sizeof(int);
+ eventPayload[3].DataPointer = ((IntPtr) (&isExceptionalInt));
+ WriteEventCore(TASKCOMPLETED_ID, 4, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ #region TaskWaitBegin
+ /// <summary>
+ /// Fired when starting to wait for a taks's completion explicitly or implicitly.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ /// <param name="Behavior">Configured behavior for the wait.</param>
+ /// <param name="ContinueWithTaskID">If known, if 'TaskID' has a 'continueWith' task, mention give its ID here.
+ /// 0 means unknown. This allows better visualization of the common sequential chaining case.</param>
+ /// </summary>
+ [SecuritySafeCritical]
+ [Event(TASKWAITBEGIN_ID, Version=3, Task = TplEtwProvider.Tasks.TaskWait, Opcode = EventOpcode.Send,
+ Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)]
+ public void TaskWaitBegin(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int TaskID, TaskWaitBehavior Behavior, int ContinueWithTaskID, int appDomain)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks))
+ {
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[5];
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr)(&TaskID));
+ eventPayload[3].Size = sizeof(int);
+ eventPayload[3].DataPointer = ((IntPtr)(&Behavior));
+ eventPayload[4].Size = sizeof(int);
+ eventPayload[4].DataPointer = ((IntPtr)(&ContinueWithTaskID));
+ if (TasksSetActivityIds)
+ {
+ Guid childActivityId = CreateGuidForTaskID(TaskID);
+ WriteEventWithRelatedActivityIdCore(TASKWAITBEGIN_ID, &childActivityId, 5, eventPayload);
+ }
+ else
+ WriteEventCore(TASKWAITBEGIN_ID, 5, eventPayload);
+ }
+ }
+ }
+ #endregion
+
+ /// <summary>
+ /// Fired when the wait for a tasks completion returns.
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ [Event(TASKWAITEND_ID,
+ Level = EventLevel.Verbose, Keywords = Keywords.Tasks)]
+ public void TaskWaitEnd(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int TaskID)
+ {
+ // Log an event if indicated.
+ if (IsEnabled() && IsEnabled(EventLevel.Verbose, Keywords.Tasks))
+ WriteEvent(TASKWAITEND_ID, OriginatingTaskSchedulerID, OriginatingTaskID, TaskID);
+ }
+
+ /// <summary>
+ /// Fired when the the work (method) associated with a TaskWaitEnd completes
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ [Event(TASKWAITCONTINUATIONCOMPLETE_ID,
+ Level = EventLevel.Verbose, Keywords = Keywords.TaskStops)]
+ public void TaskWaitContinuationComplete(int TaskID)
+ {
+ // Log an event if indicated.
+ if (IsEnabled() && IsEnabled(EventLevel.Verbose, Keywords.Tasks))
+ WriteEvent(TASKWAITCONTINUATIONCOMPLETE_ID, TaskID);
+ }
+
+ /// <summary>
+ /// Fired when the the work (method) associated with a TaskWaitEnd completes
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The task ID.</param>
+ [Event(TASKWAITCONTINUATIONSTARTED_ID,
+ Level = EventLevel.Verbose, Keywords = Keywords.TaskStops)]
+ public void TaskWaitContinuationStarted(int TaskID)
+ {
+ // Log an event if indicated.
+ if (IsEnabled() && IsEnabled(EventLevel.Verbose, Keywords.Tasks))
+ WriteEvent(TASKWAITCONTINUATIONSTARTED_ID, TaskID);
+ }
+
+ /// <summary>
+ /// Fired when the an asynchronous continuation for a task is scheduled
+ /// </summary>
+ /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
+ /// <param name="OriginatingTaskID">The task ID.</param>
+ /// <param name="TaskID">The activityId for the continuation.</param>
+ [SecuritySafeCritical]
+ [Event(AWAITTASKCONTINUATIONSCHEDULED_ID, Task = Tasks.AwaitTaskContinuationScheduled, Opcode = EventOpcode.Send,
+ Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)]
+ public void AwaitTaskContinuationScheduled(
+ int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER
+ int ContinuwWithTaskId)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks))
+ {
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[3];
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID));
+ eventPayload[1].Size = sizeof(int);
+ eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID));
+ eventPayload[2].Size = sizeof(int);
+ eventPayload[2].DataPointer = ((IntPtr) (&ContinuwWithTaskId));
+ if (TasksSetActivityIds)
+ {
+ Guid continuationActivityId = CreateGuidForTaskID(ContinuwWithTaskId);
+ WriteEventWithRelatedActivityIdCore(AWAITTASKCONTINUATIONSCHEDULED_ID, &continuationActivityId, 3, eventPayload);
+ }
+ else
+ WriteEventCore(AWAITTASKCONTINUATIONSCHEDULED_ID, 3, eventPayload);
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [Event(TRACEOPERATIONSTART_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)]
+ public void TraceOperationBegin(int TaskID, string OperationName, long RelatedContext)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation))
+ {
+ unsafe
+ {
+ fixed(char* operationNamePtr = OperationName)
+ {
+ EventData* eventPayload = stackalloc EventData[3];
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&TaskID));
+
+ eventPayload[1].Size = ((OperationName.Length + 1) * 2);
+ eventPayload[1].DataPointer = ((IntPtr) operationNamePtr);
+
+ eventPayload[2].Size = sizeof(long);
+ eventPayload[2].DataPointer = ((IntPtr) (&RelatedContext));
+ WriteEventCore(TRACEOPERATIONSTART_ID, 3, eventPayload);
+ }
+ }
+ }
+ }
+
+ [SecuritySafeCritical]
+ [Event(TRACEOPERATIONRELATION_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityRelation)]
+ public void TraceOperationRelation(int TaskID, CausalityRelation Relation)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityRelation))
+ WriteEvent(TRACEOPERATIONRELATION_ID, TaskID,(int) Relation); // optmized overload for this exists
+ }
+
+ [SecuritySafeCritical]
+ [Event(TRACEOPERATIONSTOP_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)]
+ public void TraceOperationEnd(int TaskID, AsyncCausalityStatus Status)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation))
+ WriteEvent(TRACEOPERATIONSTOP_ID, TaskID,(int) Status); // optmized overload for this exists
+ }
+
+ [SecuritySafeCritical]
+ [Event(TRACESYNCHRONOUSWORKSTART_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)]
+ public void TraceSynchronousWorkBegin(int TaskID, CausalitySynchronousWork Work)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork))
+ WriteEvent(TRACESYNCHRONOUSWORKSTART_ID, TaskID,(int) Work); // optmized overload for this exists
+ }
+
+ [SecuritySafeCritical]
+ [Event(TRACESYNCHRONOUSWORKSTOP_ID, Version=1,
+ Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)]
+ public void TraceSynchronousWorkEnd(CausalitySynchronousWork Work)
+ {
+ if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork))
+ {
+ unsafe
+ {
+ EventData* eventPayload = stackalloc EventData[1];
+ eventPayload[0].Size = sizeof(int);
+ eventPayload[0].DataPointer = ((IntPtr) (&Work));
+
+ WriteEventCore(TRACESYNCHRONOUSWORKSTOP_ID, 1, eventPayload);
+ }
+ }
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ unsafe public void RunningContinuation(int TaskID, object Object) { RunningContinuation(TaskID, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); }
+ [Event(20, Keywords = Keywords.Debug)]
+ private void RunningContinuation(int TaskID, long Object)
+ {
+ if (Debug)
+ WriteEvent(20, TaskID, Object);
+ }
+
+ [NonEvent, System.Security.SecuritySafeCritical]
+ unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); }
+
+ [Event(21, Keywords = Keywords.Debug)]
+ public void RunningContinuationList(int TaskID, int Index, long Object)
+ {
+ if (Debug)
+ WriteEvent(21, TaskID, Index, Object);
+ }
+
+ [Event(22, Keywords = Keywords.Debug)]
+ public void DebugMessage(string Message) { WriteEvent(22, Message); }
+
+ [Event(23, Keywords = Keywords.Debug)]
+ public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(23, Facility, Message); }
+
+ [Event(24, Keywords = Keywords.Debug)]
+ public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); }
+
+ [Event(25, Keywords = Keywords.DebugActivityId)]
+ public void SetActivityId(Guid NewId)
+ {
+ if (DebugActivityId)
+ WriteEvent(25, NewId);
+ }
+
+ [Event(26, Keywords = Keywords.Debug)]
+ public void NewID(int TaskID)
+ {
+ if (Debug)
+ WriteEvent(26, TaskID);
+ }
+
+ /// <summary>
+ /// Activity IDs are GUIDS but task IDS are integers (and are not unique across appdomains
+ /// This routine creates a process wide unique GUID given a task ID
+ /// </summary>
+ internal static Guid CreateGuidForTaskID(int taskID)
+ {
+ // The thread pool generated a process wide unique GUID from a task GUID by
+ // using the taskGuid, the appdomain ID, and 8 bytes of 'randomization' chosen by
+ // using the last 8 bytes as the provider GUID for this provider.
+ // These were generated by CreateGuid, and are reasonably random (and thus unlikley to collide
+ uint pid = EventSource.s_currentPid;
+ int appDomainID = System.Threading.Thread.GetDomainID();
+ return new Guid(taskID,
+ (short) appDomainID , (short) (appDomainID >> 16),
+ (byte)pid, (byte)(pid >> 8), (byte)(pid >> 16), (byte)(pid >> 24),
+ 0xff, 0xdc, 0xd7, 0xb5);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs
new file mode 100644
index 0000000000..36f8401a4d
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs
@@ -0,0 +1,7344 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A schedulable unit of work.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.ExceptionServices;
+using System.Security;
+using System.Security.Permissions;
+using System.Threading;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using Microsoft.Win32;
+using System.Diagnostics.Tracing;
+
+// Disable the "reference to volatile field not treated as volatile" error.
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+
+ /// <summary>
+ /// Utility class for allocating structs as heap variables
+ /// </summary>
+ internal class Shared<T>
+ {
+ internal T Value;
+
+ internal Shared(T value)
+ {
+ this.Value = value;
+ }
+
+ }
+
+ /// <summary>
+ /// Represents the current stage in the lifecycle of a <see cref="Task"/>.
+ /// </summary>
+ public enum TaskStatus
+ {
+ /// <summary>
+ /// The task has been initialized but has not yet been scheduled.
+ /// </summary>
+ Created,
+ /// <summary>
+ /// The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
+ /// </summary>
+ WaitingForActivation,
+ /// <summary>
+ /// The task has been scheduled for execution but has not yet begun executing.
+ /// </summary>
+ WaitingToRun,
+ /// <summary>
+ /// The task is running but has not yet completed.
+ /// </summary>
+ Running,
+ // /// <summary>
+ // /// The task is currently blocked in a wait state.
+ // /// </summary>
+ // Blocked,
+ /// <summary>
+ /// The task has finished executing and is implicitly waiting for
+ /// attached child tasks to complete.
+ /// </summary>
+ WaitingForChildrenToComplete,
+ /// <summary>
+ /// The task completed execution successfully.
+ /// </summary>
+ RanToCompletion,
+ /// <summary>
+ /// The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken
+ /// while the token was in signaled state, or the task's CancellationToken was already signaled before the
+ /// task started executing.
+ /// </summary>
+ Canceled,
+ /// <summary>
+ /// The task completed due to an unhandled exception.
+ /// </summary>
+ Faulted
+ }
+
+ /// <summary>
+ /// Represents an asynchronous operation.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// <see cref="Task"/> instances may be created in a variety of ways. The most common approach is by
+ /// using the Task type's <see cref="Factory"/> property to retrieve a <see
+ /// cref="System.Threading.Tasks.TaskFactory"/> instance that can be used to create tasks for several
+ /// purposes. For example, to create a <see cref="Task"/> that runs an action, the factory's StartNew
+ /// method may be used:
+ /// <code>
+ /// // C#
+ /// var t = Task.Factory.StartNew(() => DoAction());
+ ///
+ /// ' Visual Basic
+ /// Dim t = Task.Factory.StartNew(Function() DoAction())
+ /// </code>
+ /// </para>
+ /// <para>
+ /// The <see cref="Task"/> class also provides constructors that initialize the Task but that do not
+ /// schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the
+ /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation
+ /// and scheduling must be separated, the constructors may be used, and the task's <see cref="Start()"/>
+ /// method may then be used to schedule the task for execution at a later time.
+ /// </para>
+ /// <para>
+ /// All members of <see cref="Task"/>, except for <see cref="Dispose()"/>, are thread-safe
+ /// and may be used from multiple threads concurrently.
+ /// </para>
+ /// <para>
+ /// For operations that return values, the <see cref="System.Threading.Tasks.Task{TResult}"/> class
+ /// should be used.
+ /// </para>
+ /// <para>
+ /// For developers implementing custom debuggers, several internal and private members of Task may be
+ /// useful (these may change from release to release). The Int32 m_taskId field serves as the backing
+ /// store for the <see cref="Id"/> property, however accessing this field directly from a debugger may be
+ /// more efficient than accessing the same value through the property's getter method (the
+ /// s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). Similarly, the
+ /// Int32 m_stateFlags field stores information about the current lifecycle stage of the Task,
+ /// information also accessible through the <see cref="Status"/> property. The m_action System.Object
+ /// field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the
+ /// async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the
+ /// InternalWait method serves a potential marker for when a Task is entering a wait operation.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerTypeProxy(typeof(SystemThreadingTasks_TaskDebugView))]
+ [DebuggerDisplay("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}")]
+ public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
+ {
+ [ThreadStatic]
+ internal static Task t_currentTask; // The currently executing task.
+ [ThreadStatic]
+ private static StackGuard t_stackGuard; // The stack guard object for this thread
+
+ internal static int s_taskIdCounter; //static counter used to generate unique task IDs
+ private readonly static TaskFactory s_factory = new TaskFactory();
+
+ private volatile int m_taskId; // this task's unique ID. initialized only if it is ever requested
+
+ internal object m_action; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func.
+ // If m_action is set to null it will indicate that we operate in the
+ // "externally triggered completion" mode, which is exclusively meant
+ // for the signalling Task<TResult> (aka. promise). In this mode,
+ // we don't call InnerInvoke() in response to a Wait(), but simply wait on
+ // the completion event which will be set when the Future class calls Finish().
+ // But the event would now be signalled if Cancel() is called
+
+
+ internal object m_stateObject; // A state object that can be optionally supplied, passed to action.
+ internal TaskScheduler m_taskScheduler; // The task scheduler this task runs under.
+
+ internal volatile int m_stateFlags;
+
+ private Task ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent
+ private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags
+
+ // State constants for m_stateFlags;
+ // The bits of m_stateFlags are allocated as follows:
+ // 0x40000000 - TaskBase state flag
+ // 0x3FFF0000 - Task state flags
+ // 0x0000FF00 - internal TaskCreationOptions flags
+ // 0x000000FF - publicly exposed TaskCreationOptions flags
+ //
+ // See TaskCreationOptions for bit values associated with TaskCreationOptions
+ //
+ private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111
+ internal const int TASK_STATE_STARTED = 0x10000; //bin: 0000 0000 0000 0001 0000 0000 0000 0000
+ internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; //bin: 0000 0000 0000 0010 0000 0000 0000 0000
+ internal const int TASK_STATE_DISPOSED = 0x40000; //bin: 0000 0000 0000 0100 0000 0000 0000 0000
+ internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; //bin: 0000 0000 0000 1000 0000 0000 0000 0000
+ internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; //bin: 0000 0000 0001 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_FAULTED = 0x200000; //bin: 0000 0000 0010 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_CANCELED = 0x400000; //bin: 0000 0000 0100 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; //bin: 0000 0000 1000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; //bin: 0000 0001 0000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; //bin: 0000 0010 0000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; //bin: 0000 0100 0000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000; //bin: 0000 1000 0000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; //bin: 0001 0000 0000 0000 0000 0000 0000 0000
+ //This could be moved to InternalTaskOptions enum
+ internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; //bin: 0010 0000 0000 0000 0000 0000 0000 0000
+ internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; //bin: 0100 0000 0000 0000 0000 0000 0000 0000
+
+ // A mask for all of the final states a task may be in
+ private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION;
+
+ // Values for ContingentProperties.m_internalCancellationRequested.
+ private const int CANCELLATION_REQUESTED = 0x1;
+
+ // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel,
+ // in that order. The logic arround this object assumes it will never regress to a previous state.
+ private volatile object m_continuationObject = null;
+
+ // m_continuationObject is set to this when the task completes.
+ private static readonly object s_taskCompletionSentinel = new object();
+
+ // A private flag that would be set (only) by the debugger
+ // When true the Async Causality logging trace is enabled as well as a dictionary to relate operation ids with Tasks
+ [FriendAccessAllowed]
+ internal static bool s_asyncDebuggingEnabled; //false by default
+
+ // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
+ // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
+ private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
+ private static readonly Object s_activeTasksLock = new Object();
+
+ // These methods are a way to access the dictionary both from this class and for other classes that also
+ // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<>
+ [FriendAccessAllowed]
+ internal static bool AddToActiveTasks(Task task)
+ {
+ Contract.Requires(task != null, "Null Task objects can't be added to the ActiveTasks collection");
+ lock (s_activeTasksLock)
+ {
+ s_currentActiveTasks[task.Id] = task;
+ }
+ //always return true to keep signature as bool for backwards compatibility
+ return true;
+ }
+
+ [FriendAccessAllowed]
+ internal static void RemoveFromActiveTasks(int taskId)
+ {
+ lock (s_activeTasksLock)
+ {
+ s_currentActiveTasks.Remove(taskId);
+ }
+ }
+
+ // We moved a number of Task properties into this class. The idea is that in most cases, these properties never
+ // need to be accessed during the life cycle of a Task, so we don't want to instantiate them every time. Once
+ // one of these properties needs to be written, we will instantiate a ContingentProperties object and set
+ // the appropriate property.
+ internal class ContingentProperties
+ {
+ // Additional context
+
+ internal ExecutionContext m_capturedContext; // The execution context to run the task within, if any. Only set from non-concurrent contexts.
+
+ // Completion fields (exceptions and event)
+
+ internal volatile ManualResetEventSlim m_completionEvent; // Lazily created if waiting is required.
+ internal volatile TaskExceptionHolder m_exceptionsHolder; // Tracks exceptions, if any have occurred
+
+ // Cancellation fields (token, registration, and internally requested)
+
+ internal CancellationToken m_cancellationToken; // Task's cancellation token, if it has one
+ internal Shared<CancellationTokenRegistration> m_cancellationRegistration; // Task's registration with the cancellation token
+ internal volatile int m_internalCancellationRequested; // Its own field because multiple threads legally try to set it.
+
+ // Parenting fields
+
+ // # of active children + 1 (for this task itself).
+ // Used for ensuring all children are done before this task can complete
+ // The extra count helps prevent the race condition for executing the final state transition
+ // (i.e. whether the last child or this task itself should call FinishStageTwo())
+ internal volatile int m_completionCountdown = 1;
+ // A list of child tasks that threw an exception (TCEs don't count),
+ // but haven't yet been waited on by the parent, lazily initialized.
+ internal volatile List<Task> m_exceptionalChildren;
+ // A task's parent, or null if parent-less. Only set during Task construction.
+ internal Task m_parent;
+
+ /// <summary>
+ /// Sets the internal completion event.
+ /// </summary>
+ internal void SetCompleted()
+ {
+ var mres = m_completionEvent;
+ if (mres != null) mres.Set();
+ }
+
+ /// <summary>
+ /// Checks if we registered a CT callback during construction, and deregisters it.
+ /// This should be called when we know the registration isn't useful anymore. Specifically from Finish() if the task has completed
+ /// successfully or with an exception.
+ /// </summary>
+ internal void DeregisterCancellationCallback()
+ {
+ if (m_cancellationRegistration != null)
+ {
+ // Harden against ODEs thrown from disposing of the CTR.
+ // Since the task has already been put into a final state by the time this
+ // is called, all we can do here is suppress the exception.
+ try { m_cancellationRegistration.Value.Dispose(); }
+ catch (ObjectDisposedException) { }
+ m_cancellationRegistration = null;
+ }
+ }
+ }
+
+
+ // This field will only be instantiated to some non-null value if any ContingentProperties need to be set.
+ // This will be a ContingentProperties instance or a type derived from it
+ internal ContingentProperties m_contingentProperties;
+
+ // Special internal constructor to create an already-completed task.
+ // if canceled==true, create a Canceled task, or else create a RanToCompletion task.
+ // Constructs the task as already completed
+ internal Task(bool canceled, TaskCreationOptions creationOptions, CancellationToken ct)
+ {
+ int optionFlags = (int)creationOptions;
+ if (canceled)
+ {
+ m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags;
+ m_contingentProperties = new ContingentProperties() // can't have children, so just instantiate directly
+ {
+ m_cancellationToken = ct,
+ m_internalCancellationRequested = CANCELLATION_REQUESTED,
+ };
+ }
+ else
+ m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags;
+ }
+
+ /// <summary>Constructor for use with promise-style tasks that aren't configurable.</summary>
+ internal Task()
+ {
+ m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | (int)InternalTaskOptions.PromiseTask;
+ }
+
+ // Special constructor for use with promise-style tasks.
+ // Added promiseStyle parameter as an aid to the compiler to distinguish between (state,TCO) and
+ // (action,TCO). It should always be true.
+ internal Task(object state, TaskCreationOptions creationOptions, bool promiseStyle)
+ {
+ Contract.Assert(promiseStyle, "Promise CTOR: promiseStyle was false");
+
+ // Check the creationOptions. We allow the AttachedToParent option to be specified for promise tasks.
+ // Also allow RunContinuationsAsynchronously because this is the constructor called by TCS
+ if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions);
+ }
+
+ // Only set a parent if AttachedToParent is specified.
+ if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
+ {
+ Task parent = Task.InternalCurrent;
+ if (parent != null)
+ {
+ EnsureContingentPropertiesInitializedUnsafe().m_parent = parent;
+ }
+ }
+
+ TaskConstructorCore(null, state, default(CancellationToken), creationOptions, InternalTaskOptions.PromiseTask, null);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the Task.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action action)
+ : this(action, null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action and <see cref="System.Threading.CancellationToken">CancellationToken</see>.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the Task.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new Task.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action action, CancellationToken cancellationToken)
+ : this(action, null, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action and creation options.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the Task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action action, TaskCreationOptions creationOptions)
+ : this(action, null, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action and creation options.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the Task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
+ : this(action, null, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action and state.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="state">An object representing data to be used by the action.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action<object> action, object state)
+ : this(action, state, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="state">An object representing data to be used by the action.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action<object> action, object state, CancellationToken cancellationToken)
+ : this(action, state, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="state">An object representing data to be used by the action.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the Task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action<object> action, object state, TaskCreationOptions creationOptions)
+ : this(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// </summary>
+ /// <param name="action">The delegate that represents the code to execute in the task.</param>
+ /// <param name="state">An object representing data to be used by the action.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the Task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
+ : this(action, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ internal Task(Action<object> action, object state, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ : this(action, state, parent, cancellationToken, creationOptions, internalOptions, scheduler)
+ {
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// An internal constructor used by the factory methods on task and its descendent(s).
+ /// This variant does not capture the ExecutionContext; it is up to the caller to do that.
+ /// </summary>
+ /// <param name="action">An action to execute.</param>
+ /// <param name="state">Optional state to pass to the action.</param>
+ /// <param name="parent">Parent of Task.</param>
+ /// <param name="cancellationToken">A CancellationToken for the task.</param>
+ /// <param name="scheduler">A task scheduler under which the task will run.</param>
+ /// <param name="creationOptions">Options to control its execution.</param>
+ /// <param name="internalOptions">Internal options to control its execution</param>
+ internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
+ {
+ if (action == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
+ }
+ Contract.EndContractBlock();
+
+ // Keep a link to your parent if: (A) You are attached, or (B) you are self-replicating.
+ if (parent != null &&
+ ((creationOptions & TaskCreationOptions.AttachedToParent) != 0 ||
+ (internalOptions & InternalTaskOptions.SelfReplicating) != 0))
+ {
+ EnsureContingentPropertiesInitializedUnsafe().m_parent = parent;
+ }
+
+ TaskConstructorCore(action, state, cancellationToken, creationOptions, internalOptions, scheduler);
+ }
+
+ /// <summary>
+ /// Common logic used by the following internal ctors:
+ /// Task()
+ /// Task(object action, object state, Task parent, TaskCreationOptions options, TaskScheduler taskScheduler)
+ /// </summary>
+ /// <param name="action">Action for task to execute.</param>
+ /// <param name="state">Object to which to pass to action (may be null)</param>
+ /// <param name="scheduler">Task scheduler on which to run thread (only used by continuation tasks).</param>
+ /// <param name="cancellationToken">A CancellationToken for the Task.</param>
+ /// <param name="creationOptions">Options to customize behavior of Task.</param>
+ /// <param name="internalOptions">Internal options to customize behavior of Task.</param>
+ internal void TaskConstructorCore(object action, object state, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
+ {
+ m_action = action;
+ m_stateObject = state;
+ m_taskScheduler = scheduler;
+
+ // Check for validity of options
+ if ((creationOptions &
+ ~(TaskCreationOptions.AttachedToParent |
+ TaskCreationOptions.LongRunning |
+ TaskCreationOptions.DenyChildAttach |
+ TaskCreationOptions.HideScheduler |
+ TaskCreationOptions.PreferFairness |
+ TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions);
+ }
+
+#if DEBUG
+ // Check the validity of internalOptions
+ int illegalInternalOptions =
+ (int) (internalOptions &
+ ~(InternalTaskOptions.SelfReplicating |
+ InternalTaskOptions.ChildReplica |
+ InternalTaskOptions.PromiseTask |
+ InternalTaskOptions.ContinuationTask |
+ InternalTaskOptions.LazyCancellation |
+ InternalTaskOptions.QueuedByRuntime));
+ Contract.Assert(illegalInternalOptions == 0, "TaskConstructorCore: Illegal internal options");
+#endif
+
+ // Throw exception if the user specifies both LongRunning and SelfReplicating
+ if (((creationOptions & TaskCreationOptions.LongRunning) != 0) &&
+ ((internalOptions & InternalTaskOptions.SelfReplicating) != 0))
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_ctor_LRandSR);
+ }
+
+ // Assign options to m_stateAndOptionsFlag.
+ Contract.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags");
+ Contract.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits");
+ var tmpFlags = (int)creationOptions | (int)internalOptions;
+ if ((m_action == null) || ((internalOptions & InternalTaskOptions.ContinuationTask) != 0))
+ {
+ // For continuation tasks or TaskCompletionSource.Tasks, begin life in the
+ // WaitingForActivation state rather than the Created state.
+ tmpFlags |= TASK_STATE_WAITINGFORACTIVATION;
+ }
+ m_stateFlags = tmpFlags; // one write to the volatile m_stateFlags instead of two when setting the above options
+
+ // Now is the time to add the new task to the children list
+ // of the creating task if the options call for it.
+ // We can safely call the creator task's AddNewChild() method to register it,
+ // because at this point we are already on its thread of execution.
+
+ Task parent = m_contingentProperties?.m_parent;
+ if (parent != null
+ && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
+ && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
+ )
+ {
+ parent.AddNewChild();
+ }
+
+ // if we have a non-null cancellationToken, allocate the contingent properties to save it
+ // we need to do this as the very last thing in the construction path, because the CT registration could modify m_stateFlags
+ if (cancellationToken.CanBeCanceled)
+ {
+ Contract.Assert((internalOptions &
+ (InternalTaskOptions.ChildReplica | InternalTaskOptions.SelfReplicating | InternalTaskOptions.ContinuationTask)) == 0,
+ "TaskConstructorCore: Did not expect to see cancelable token for replica/replicating or continuation task.");
+
+ AssignCancellationToken(cancellationToken, null, null);
+ }
+ }
+
+ /// <summary>
+ /// Handles everything needed for associating a CancellationToken with a task which is being constructed.
+ /// This method is meant to be be called either from the TaskConstructorCore or from ContinueWithCore.
+ /// </summary>
+ private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation)
+ {
+ // There is no need to worry about concurrency issues here because we are in the constructor path of the task --
+ // there should not be any race conditions to set m_contingentProperties at this point.
+ ContingentProperties props = EnsureContingentPropertiesInitializedUnsafe();
+ props.m_cancellationToken = cancellationToken;
+
+ try
+ {
+ if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ {
+ cancellationToken.ThrowIfSourceDisposed();
+ }
+
+ // If an unstarted task has a valid CancellationToken that gets signalled while the task is still not queued
+ // we need to proactively cancel it, because it may never execute to transition itself.
+ // The only way to accomplish this is to register a callback on the CT.
+ // We exclude Promise tasks from this, because TaskCompletionSource needs to fully control the inner tasks's lifetime (i.e. not allow external cancellations)
+ if ((((InternalTaskOptions)Options &
+ (InternalTaskOptions.QueuedByRuntime | InternalTaskOptions.PromiseTask | InternalTaskOptions.LazyCancellation)) == 0))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ // Fast path for an already-canceled cancellationToken
+ this.InternalCancel(false);
+ }
+ else
+ {
+ // Regular path for an uncanceled cancellationToken
+ CancellationTokenRegistration ctr;
+ if (antecedent == null)
+ {
+ // if no antecedent was specified, use this task's reference as the cancellation state object
+ ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback, this);
+ }
+ else
+ {
+ // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple
+ // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that
+ // antecedent.RemoveCancellation(continuation) can be invoked.
+ ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback,
+ new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
+ }
+
+ props.m_cancellationRegistration = new Shared<CancellationTokenRegistration>(ctr);
+ }
+ }
+ }
+ catch
+ {
+ // If we have an exception related to our CancellationToken, then we need to subtract ourselves
+ // from our parent before throwing it.
+ Task parent = m_contingentProperties?.m_parent;
+ if ((parent != null) &&
+ ((Options & TaskCreationOptions.AttachedToParent) != 0)
+ && ((parent.Options & TaskCreationOptions.DenyChildAttach) == 0))
+ {
+ parent.DisregardChild();
+ }
+ throw;
+ }
+ }
+
+
+ // Static delegate to be used as a cancellation callback on unstarted tasks that have a valid cancellation token.
+ // This is necessary to transition them into canceled state if their cancellation token is signalled while they are still not queued
+ private readonly static Action<Object> s_taskCancelCallback = new Action<Object>(TaskCancelCallback);
+ private static void TaskCancelCallback(Object o)
+ {
+ var targetTask = o as Task;
+ if (targetTask == null)
+ {
+ var tuple = o as Tuple<Task, Task, TaskContinuation>;
+ if (tuple != null)
+ {
+ targetTask = tuple.Item1;
+
+ Task antecedentTask = tuple.Item2;
+ TaskContinuation continuation = tuple.Item3;
+ antecedentTask.RemoveContinuation(continuation);
+ }
+ }
+ Contract.Assert(targetTask != null,
+ "targetTask should have been non-null, with the supplied argument being a task or a tuple containing one");
+ targetTask.InternalCancel(false);
+ }
+
+ // Debugger support
+ private string DebuggerDisplayMethodDescription
+ {
+ get
+ {
+ Delegate d = (Delegate)m_action;
+ return d != null ? d.Method.ToString() : "{null}";
+ }
+ }
+
+
+ /// <summary>
+ /// Captures the ExecutionContext so long as flow isn't suppressed.
+ /// </summary>
+ /// <param name="stackMark">A stack crawl mark pointing to the frame of the caller.</param>
+
+ [SecuritySafeCritical]
+ internal void PossiblyCaptureContext(ref StackCrawlMark stackMark)
+ {
+ Contract.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null,
+ "Captured an ExecutionContext when one was already captured.");
+
+ // In the legacy .NET 3.5 build, we don't have the optimized overload of Capture()
+ // available, so we call the parameterless overload.
+ CapturedContext = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
+ }
+
+ // Internal property to process TaskCreationOptions access and mutation.
+ internal TaskCreationOptions Options
+ {
+ get
+ {
+ int stateFlags = m_stateFlags; // "cast away" volatility to enable inlining of OptionsMethod
+ return OptionsMethod(stateFlags);
+ }
+ }
+
+ // Similar to Options property, but allows for the use of a cached flags value rather than
+ // a read of the volatile m_stateFlags field.
+ internal static TaskCreationOptions OptionsMethod(int flags)
+ {
+ Contract.Assert((OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get");
+ return (TaskCreationOptions)(flags & OptionsMask);
+ }
+
+ // Atomically OR-in newBits to m_stateFlags, while making sure that
+ // no illegalBits are set. Returns true on success, false on failure.
+ internal bool AtomicStateUpdate(int newBits, int illegalBits)
+ {
+ // This could be implemented in terms of:
+ // internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags);
+ // but for high-throughput perf, that delegation's cost is noticeable.
+
+ SpinWait sw = new SpinWait();
+ do
+ {
+ int oldFlags = m_stateFlags;
+ if ((oldFlags & illegalBits) != 0) return false;
+ if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
+ {
+ return true;
+ }
+ sw.SpinOnce();
+ } while (true);
+ }
+
+ internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags)
+ {
+ SpinWait sw = new SpinWait();
+ do
+ {
+ oldFlags = m_stateFlags;
+ if ((oldFlags & illegalBits) != 0) return false;
+ if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
+ {
+ return true;
+ }
+ sw.SpinOnce();
+ } while (true);
+ }
+
+ /// <summary>
+ /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit.
+ /// The debugger sets this bit to aid it in "stepping out" of an async method body.
+ /// If enabled is true, this must only be called on a task that has not yet been completed.
+ /// If enabled is false, this may be called on completed tasks.
+ /// Either way, it should only be used for promise-style tasks.
+ /// </summary>
+ /// <param name="enabled">true to set the bit; false to unset the bit.</param>
+ internal void SetNotificationForWaitCompletion(bool enabled)
+ {
+ Contract.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0,
+ "Should only be used for promise-style tasks"); // hasn't been vetted on other kinds as there hasn't been a need
+
+ if (enabled)
+ {
+ // Atomically set the END_AWAIT_NOTIFICATION bit
+ bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION,
+ TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED);
+ Contract.Assert(success, "Tried to set enabled on completed Task");
+ }
+ else
+ {
+ // Atomically clear the END_AWAIT_NOTIFICATION bit
+ SpinWait sw = new SpinWait();
+ while (true)
+ {
+ int oldFlags = m_stateFlags;
+ int newFlags = oldFlags & (~TASK_STATE_WAIT_COMPLETION_NOTIFICATION);
+ if (Interlocked.CompareExchange(ref m_stateFlags, newFlags, oldFlags) == oldFlags) break;
+ sw.SpinOnce();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Calls the debugger notification method if the right bit is set and if
+ /// the task itself allows for the notification to proceed.
+ /// </summary>
+ /// <returns>true if the debugger was notified; otherwise, false.</returns>
+ internal bool NotifyDebuggerOfWaitCompletionIfNecessary()
+ {
+ // Notify the debugger if of any of the tasks we've waited on requires notification
+ if (IsWaitNotificationEnabled && ShouldNotifyDebuggerOfWaitCompletion)
+ {
+ NotifyDebuggerOfWaitCompletion();
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>Returns true if any of the supplied tasks require wait notification.</summary>
+ /// <param name="tasks">The tasks to check.</param>
+ /// <returns>true if any of the tasks require notification; otherwise, false.</returns>
+ internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task[] tasks)
+ {
+ Contract.Assert(tasks != null, "Expected non-null array of tasks");
+ foreach (var task in tasks)
+ {
+ if (task != null &&
+ task.IsWaitNotificationEnabled &&
+ task.ShouldNotifyDebuggerOfWaitCompletion) // potential recursion
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// <summary>Gets whether either the end await bit is set or (not xor) the task has not completed successfully.</summary>
+ /// <returns>(DebuggerBitSet || !RanToCompletion)</returns>
+ internal bool IsWaitNotificationEnabledOrNotRanToCompletion
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return (m_stateFlags & (Task.TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task.TASK_STATE_RAN_TO_COMPLETION))
+ != Task.TASK_STATE_RAN_TO_COMPLETION;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether we should inform the debugger that we're ending a join with a task.
+ /// This should only be called if the debugger notification bit is set, as it is has some cost,
+ /// namely it is a virtual call (however calling it if the bit is not set is not functionally
+ /// harmful). Derived implementations may choose to only conditionally call down to this base
+ /// implementation.
+ /// </summary>
+ internal virtual bool ShouldNotifyDebuggerOfWaitCompletion // ideally would be familyAndAssembly, but that can't be done in C#
+ {
+ get
+ {
+ // It's theoretically possible but extremely rare that this assert could fire because the
+ // bit was unset between the time that it was checked and this method was called.
+ // It's so remote a chance that it's worth having the assert to protect against misuse.
+ bool isWaitNotificationEnabled = IsWaitNotificationEnabled;
+ Contract.Assert(isWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
+ return isWaitNotificationEnabled;
+ }
+ }
+
+ /// <summary>Gets whether the task's debugger notification for wait completion bit is set.</summary>
+ /// <returns>true if the bit is set; false if it's not set.</returns>
+ internal bool IsWaitNotificationEnabled // internal only to enable unit tests; would otherwise be private
+ {
+ get { return (m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION) != 0; }
+ }
+
+ /// <summary>Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized.</summary>
+ /// <remarks>All joins with a task should end up calling this if their debugger notification bit is set.</remarks>
+ [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+ private void NotifyDebuggerOfWaitCompletion()
+ {
+ // It's theoretically possible but extremely rare that this assert could fire because the
+ // bit was unset between the time that it was checked and this method was called.
+ // It's so remote a chance that it's worth having the assert to protect against misuse.
+ Contract.Assert(IsWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
+
+ // Now that we're notifying the debugger, clear the bit. The debugger should do this anyway,
+ // but this adds a bit of protection in case it fails to, and given that the debugger is involved,
+ // the overhead here for the interlocked is negligable. We do still rely on the debugger
+ // to clear bits, as this doesn't recursively clear bits in the case of, for example, WhenAny.
+ SetNotificationForWaitCompletion(enabled: false);
+ }
+
+
+ // Atomically mark a Task as started while making sure that it is not canceled.
+ internal bool MarkStarted()
+ {
+ return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool FireTaskScheduledIfNeeded(TaskScheduler ts)
+ {
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.IsEnabled() && (m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0)
+ {
+ m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED;
+
+ Task currentTask = Task.InternalCurrent;
+ Task parentTask = m_contingentProperties?.m_parent;
+ etwLog.TaskScheduled(ts.Id, currentTask == null ? 0 : currentTask.Id,
+ this.Id, parentTask == null ? 0 : parentTask.Id, (int)this.Options,
+ System.Threading.Thread.GetDomainID());
+ return true;
+ }
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Internal function that will be called by a new child task to add itself to
+ /// the children list of the parent (this).
+ ///
+ /// Since a child task can only be created from the thread executing the action delegate
+ /// of this task, reentrancy is neither required nor supported. This should not be called from
+ /// anywhere other than the task construction/initialization codepaths.
+ /// </summary>
+ internal void AddNewChild()
+ {
+ Contract.Assert(Task.InternalCurrent == this || this.IsSelfReplicatingRoot, "Task.AddNewChild(): Called from an external context");
+
+ var props = EnsureContingentPropertiesInitialized();
+
+ if (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot)
+ {
+ // A count of 1 indicates so far there was only the parent, and this is the first child task
+ // Single kid => no fuss about who else is accessing the count. Let's save ourselves 100 cycles
+ // We exclude self replicating root tasks from this optimization, because further child creation can take place on
+ // other cores and with bad enough timing this write may not be visible to them.
+ props.m_completionCountdown++;
+ }
+ else
+ {
+ // otherwise do it safely
+ Interlocked.Increment(ref props.m_completionCountdown);
+ }
+ }
+
+ // This is called in the case where a new child is added, but then encounters a CancellationToken-related exception.
+ // We need to subtract that child from m_completionCountdown, or the parent will never complete.
+ internal void DisregardChild()
+ {
+ Contract.Assert(Task.InternalCurrent == this, "Task.DisregardChild(): Called from an external context");
+
+ var props = EnsureContingentPropertiesInitialized();
+ Contract.Assert(props.m_completionCountdown >= 2, "Task.DisregardChild(): Expected parent count to be >= 2");
+ Interlocked.Decrement(ref props.m_completionCountdown);
+ }
+
+ /// <summary>
+ /// Starts the <see cref="Task"/>, scheduling it for execution to the current <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
+ /// </summary>
+ /// <remarks>
+ /// A task may only be started and run only once. Any attempts to schedule a task a second time
+ /// will result in an exception.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">
+ /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
+ /// executed, or canceled, or it may have been created in a manner that doesn't support direct
+ /// scheduling.
+ /// </exception>
+ public void Start()
+ {
+ Start(TaskScheduler.Current);
+ }
+
+ /// <summary>
+ /// Starts the <see cref="Task"/>, scheduling it for execution to the specified <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
+ /// </summary>
+ /// <remarks>
+ /// A task may only be started and run only once. Any attempts to schedule a task a second time will
+ /// result in an exception.
+ /// </remarks>
+ /// <param name="scheduler">
+ /// The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> with which to associate
+ /// and execute this task.
+ /// </param>
+ /// <exception cref="ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="InvalidOperationException">
+ /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
+ /// executed, or canceled, or it may have been created in a manner that doesn't support direct
+ /// scheduling.
+ /// </exception>
+ public void Start(TaskScheduler scheduler)
+ {
+ // Read the volatile m_stateFlags field once and cache it for subsequent operations
+ int flags = m_stateFlags;
+
+ // Need to check this before (m_action == null) because completed tasks will
+ // set m_action to null. We would want to know if this is the reason that m_action == null.
+ if (IsCompletedMethod(flags))
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_TaskCompleted);
+ }
+
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+
+ var options = OptionsMethod(flags);
+ if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_Promise);
+ }
+ if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_ContinuationTask);
+ }
+
+ // Make sure that Task only gets started once. Or else throw an exception.
+ if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_AlreadyStarted);
+ }
+
+ ScheduleAndStart(true);
+ }
+
+ /// <summary>
+ /// Runs the <see cref="Task"/> synchronously on the current <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A task may only be started and run only once. Any attempts to schedule a task a second time will
+ /// result in an exception.
+ /// </para>
+ /// <para>
+ /// Tasks executed with <see cref="RunSynchronously()"/> will be associated with the current <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
+ /// </para>
+ /// <para>
+ /// If the target scheduler does not support running this Task on the current thread, the Task will
+ /// be scheduled for execution on the scheduler, and the current thread will block until the
+ /// Task has completed execution.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">
+ /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
+ /// executed, or canceled, or it may have been created in a manner that doesn't support direct
+ /// scheduling.
+ /// </exception>
+ public void RunSynchronously()
+ {
+ InternalRunSynchronously(TaskScheduler.Current, waitForCompletion: true);
+ }
+
+ /// <summary>
+ /// Runs the <see cref="Task"/> synchronously on the <see
+ /// cref="System.Threading.Tasks.TaskScheduler">scheduler</see> provided.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A task may only be started and run only once. Any attempts to schedule a task a second time will
+ /// result in an exception.
+ /// </para>
+ /// <para>
+ /// If the target scheduler does not support running this Task on the current thread, the Task will
+ /// be scheduled for execution on the scheduler, and the current thread will block until the
+ /// Task has completed execution.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">
+ /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
+ /// executed, or canceled, or it may have been created in a manner that doesn't support direct
+ /// scheduling.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">The <paramref name="scheduler"/> parameter
+ /// is null.</exception>
+ /// <param name="scheduler">The scheduler on which to attempt to run this task inline.</param>
+ public void RunSynchronously(TaskScheduler scheduler)
+ {
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ InternalRunSynchronously(scheduler, waitForCompletion: true);
+ }
+
+ //
+ // Internal version of RunSynchronously that allows not waiting for completion.
+ //
+ [SecuritySafeCritical] // Needed for QueueTask
+ internal void InternalRunSynchronously(TaskScheduler scheduler, bool waitForCompletion)
+ {
+ Contract.Requires(scheduler != null, "Task.InternalRunSynchronously(): null TaskScheduler");
+
+ // Read the volatile m_stateFlags field once and cache it for subsequent operations
+ int flags = m_stateFlags;
+
+ // Can't call this method on a continuation task
+ var options = OptionsMethod(flags);
+ if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_Continuation);
+ }
+
+ // Can't call this method on a promise-style task
+ if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_Promise);
+ }
+
+ // Can't call this method on a task that has already completed
+ if (IsCompletedMethod(flags))
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted);
+ }
+
+ // Make sure that Task only gets started once. Or else throw an exception.
+ if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_AlreadyStarted);
+ }
+
+ // execute only if we successfully cancel when concurrent cancel attempts are made.
+ // otherwise throw an exception, because we've been canceled.
+ if (MarkStarted())
+ {
+ bool taskQueued = false;
+ try
+ {
+ // We wrap TryRunInline() in a try/catch block and move an excepted task to Faulted here,
+ // but not in Wait()/WaitAll()/FastWaitAll(). Here, we know for sure that the
+ // task will not be subsequently scheduled (assuming that the scheduler adheres
+ // to the guideline that an exception implies that no state change took place),
+ // so it is safe to catch the exception and move the task to a final state. The
+ // same cannot be said for Wait()/WaitAll()/FastWaitAll().
+ if (!scheduler.TryRunInline(this, false))
+ {
+ scheduler.InternalQueueTask(this);
+ taskQueued = true; // only mark this after successfully queuing the task.
+ }
+
+ // A successful TryRunInline doesn't guarantee completion, as there may be unfinished children.
+ // Also if we queued the task above, the task may not be done yet.
+ if (waitForCompletion && !IsCompleted)
+ {
+ SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
+ }
+ }
+ catch (Exception e)
+ {
+ // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
+ // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
+ if (!taskQueued && !(e is ThreadAbortException))
+ {
+ // We had a problem with TryRunInline() or QueueTask().
+ // Record the exception, marking ourselves as Completed/Faulted.
+ TaskSchedulerException tse = new TaskSchedulerException(e);
+ AddException(tse);
+ Finish(false);
+
+ // Mark ourselves as "handled" to avoid crashing the finalizer thread if the caller neglects to
+ // call Wait() on this task.
+ // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
+ Contract.Assert(
+ (m_contingentProperties != null) &&
+ (m_contingentProperties.m_exceptionsHolder != null) &&
+ (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
+ "Task.InternalRunSynchronously(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
+ "and to have faults recorded.");
+ m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
+
+ // And re-throw.
+ throw tse;
+ }
+ // We had a problem with waiting or this is a thread abort. Just re-throw.
+ else throw;
+ }
+ }
+ else
+ {
+ Contract.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set");
+ // Can't call this method on canceled task.
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted);
+ }
+ }
+
+
+ ////
+ //// Helper methods for Factory StartNew methods.
+ ////
+
+
+ // Implicitly converts action to object and handles the meat of the StartNew() logic.
+ internal static Task InternalStartNew(
+ Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
+ TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
+ {
+ // Validate arguments.
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ // Create and schedule the task. This throws an InvalidOperationException if already shut down.
+ // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
+ Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
+ t.PossiblyCaptureContext(ref stackMark);
+
+ t.ScheduleAndStart(false);
+ return t;
+ }
+
+ /// <summary>
+ /// Gets a unique ID for a <see cref="Task">Task</see> or task continuation instance.
+ /// </summary>
+ internal static int NewId()
+ {
+ int newId = 0;
+ // We need to repeat if Interlocked.Increment wraps around and returns 0.
+ // Otherwise next time this task's Id is queried it will get a new value
+ do
+ {
+ newId = Interlocked.Increment(ref s_taskIdCounter);
+ }
+ while (newId == 0);
+ TplEtwProvider.Log.NewID(newId);
+ return newId;
+ }
+
+
+ /////////////
+ // properties
+
+ /// <summary>
+ /// Gets a unique ID for this <see cref="Task">Task</see> instance.
+ /// </summary>
+ /// <remarks>
+ /// Task IDs are assigned on-demand and do not necessarily represent the order in the which Task
+ /// instances were created.
+ /// </remarks>
+ public int Id
+ {
+ get
+ {
+ if (m_taskId == 0)
+ {
+ int newId = NewId();
+ Interlocked.CompareExchange(ref m_taskId, newId, 0);
+ }
+
+ return m_taskId;
+ }
+ }
+
+ /// <summary>
+ /// Returns the unique ID of the currently executing <see cref="Task">Task</see>.
+ /// </summary>
+ public static int? CurrentId
+ {
+ get
+ {
+ Task currentTask = InternalCurrent;
+ if (currentTask != null)
+ return currentTask.Id;
+ else
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="Task">Task</see> instance currently executing, or
+ /// null if none exists.
+ /// </summary>
+ internal static Task InternalCurrent
+ {
+ get { return t_currentTask; }
+ }
+
+ /// <summary>
+ /// Gets the Task instance currently executing if the specified creation options
+ /// contain AttachedToParent.
+ /// </summary>
+ /// <param name="options">The options to check.</param>
+ /// <returns>The current task if there is one and if AttachToParent is in the options; otherwise, null.</returns>
+ internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
+ {
+ return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
+ }
+
+ /// <summary>
+ /// Gets the StackGuard object assigned to the current thread.
+ /// </summary>
+ internal static StackGuard CurrentStackGuard
+ {
+ get
+ {
+ StackGuard sg = t_stackGuard;
+ if (sg == null)
+ {
+ t_stackGuard = sg = new StackGuard();
+ }
+ return sg;
+ }
+ }
+
+
+ /// <summary>
+ /// Gets the <see cref="T:System.AggregateException">Exception</see> that caused the <see
+ /// cref="Task">Task</see> to end prematurely. If the <see
+ /// cref="Task">Task</see> completed successfully or has not yet thrown any
+ /// exceptions, this will return null.
+ /// </summary>
+ /// <remarks>
+ /// Tasks that throw unhandled exceptions store the resulting exception and propagate it wrapped in a
+ /// <see cref="System.AggregateException"/> in calls to <see cref="Wait()">Wait</see>
+ /// or in accesses to the <see cref="Exception"/> property. Any exceptions not observed by the time
+ /// the Task instance is garbage collected will be propagated on the finalizer thread.
+ /// </remarks>
+ public AggregateException Exception
+ {
+ get
+ {
+ AggregateException e = null;
+
+ // If you're faulted, retrieve the exception(s)
+ if (IsFaulted) e = GetExceptions(false);
+
+ // Only return an exception in faulted state (skip manufactured exceptions)
+ // A "benevolent" race condition makes it possible to return null when IsFaulted is
+ // true (i.e., if IsFaulted is set just after the check to IsFaulted above).
+ Contract.Assert((e == null) || IsFaulted, "Task.Exception_get(): returning non-null value when not Faulted");
+
+ return e;
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="T:System.Threading.Tasks.TaskStatus">TaskStatus</see> of this Task.
+ /// </summary>
+ public TaskStatus Status
+ {
+ get
+ {
+ TaskStatus rval;
+
+ // get a cached copy of the state flags. This should help us
+ // to get a consistent view of the flags if they are changing during the
+ // execution of this method.
+ int sf = m_stateFlags;
+
+ if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
+ else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
+ else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
+ else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
+ else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
+ else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
+ else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
+ else rval = TaskStatus.Created;
+
+ return rval;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether this <see cref="Task">Task</see> instance has completed
+ /// execution due to being canceled.
+ /// </summary>
+ /// <remarks>
+ /// A <see cref="Task">Task</see> will complete in Canceled state either if its <see cref="CancellationToken">CancellationToken</see>
+ /// was marked for cancellation before the task started executing, or if the task acknowledged the cancellation request on
+ /// its already signaled CancellationToken by throwing an
+ /// <see cref="System.OperationCanceledException">OperationCanceledException</see> that bears the same
+ /// <see cref="System.Threading.CancellationToken">CancellationToken</see>.
+ /// </remarks>
+ public bool IsCanceled
+ {
+ get
+ {
+ // Return true if canceled bit is set and faulted bit is not set
+ return (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED;
+ }
+ }
+
+ /// <summary>
+ /// Returns true if this task has a cancellation token and it was signaled.
+ /// To be used internally in execute entry codepaths.
+ /// </summary>
+ internal bool IsCancellationRequested
+ {
+ get
+ {
+ // check both the internal cancellation request flag and the CancellationToken attached to this task
+ var props = Volatile.Read(ref m_contingentProperties);
+ return props != null &&
+ (props.m_internalCancellationRequested == CANCELLATION_REQUESTED ||
+ props.m_cancellationToken.IsCancellationRequested);
+ }
+ }
+
+ /// <summary>
+ /// Ensures that the contingent properties field has been initialized.
+ /// ASSUMES THAT m_stateFlags IS ALREADY SET!
+ /// </summary>
+ /// <returns>The initialized contingent properties object.</returns>
+ internal ContingentProperties EnsureContingentPropertiesInitialized()
+ {
+ return LazyInitializer.EnsureInitialized(ref m_contingentProperties, () => new ContingentProperties());
+ }
+
+ /// <summary>
+ /// Without synchronization, ensures that the contingent properties field has been initialized.
+ /// ASSUMES THAT m_stateFlags IS ALREADY SET!
+ /// </summary>
+ /// <returns>The initialized contingent properties object.</returns>
+ internal ContingentProperties EnsureContingentPropertiesInitializedUnsafe()
+ {
+ return m_contingentProperties ?? (m_contingentProperties = new ContingentProperties());
+ }
+
+ /// <summary>
+ /// This internal property provides access to the CancellationToken that was set on the task
+ /// when it was constructed.
+ /// </summary>
+ internal CancellationToken CancellationToken
+ {
+ get
+ {
+ var props = Volatile.Read(ref m_contingentProperties);
+ return (props == null) ? default(CancellationToken) : props.m_cancellationToken;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether this <see cref="Task"/> threw an OperationCanceledException while its CancellationToken was signaled.
+ /// </summary>
+ internal bool IsCancellationAcknowledged
+ {
+ get { return (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; }
+ }
+
+
+ /// <summary>
+ /// Gets whether this <see cref="Task">Task</see> has completed.
+ /// </summary>
+ /// <remarks>
+ /// <see cref="IsCompleted"/> will return true when the Task is in one of the three
+ /// final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ public bool IsCompleted
+ {
+ get
+ {
+ int stateFlags = m_stateFlags; // enable inlining of IsCompletedMethod by "cast"ing away the volatility
+ return IsCompletedMethod(stateFlags);
+ }
+ }
+
+ // Similar to IsCompleted property, but allows for the use of a cached flags value
+ // rather than reading the volatile m_stateFlags field.
+ private static bool IsCompletedMethod(int flags)
+ {
+ return (flags & TASK_STATE_COMPLETED_MASK) != 0;
+ }
+
+ // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion)
+ internal bool IsRanToCompletion
+ {
+ get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="T:System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used
+ /// to create this task.
+ /// </summary>
+ public TaskCreationOptions CreationOptions
+ {
+ get { return Options & (TaskCreationOptions)(~InternalTaskOptions.InternalOptionsMask); }
+ }
+
+ /// <summary>
+ /// Gets a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait for the task to
+ /// complete.
+ /// </summary>
+ /// <remarks>
+ /// Using the wait functionality provided by <see cref="Wait()"/>
+ /// should be preferred over using <see cref="IAsyncResult.AsyncWaitHandle"/> for similar
+ /// functionality.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="Task"/> has been disposed.
+ /// </exception>
+ WaitHandle IAsyncResult.AsyncWaitHandle
+ {
+ // Although a slim event is used internally to avoid kernel resource allocation, this function
+ // forces allocation of a true WaitHandle when called.
+ get
+ {
+ bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0;
+ if (isDisposed)
+ {
+ ThrowHelper.ThrowObjectDisposedException(ExceptionResource.Task_ThrowIfDisposed);
+ }
+ return CompletedEvent.WaitHandle;
+ }
+ }
+
+ /// <summary>
+ /// Gets the state object supplied when the <see cref="Task">Task</see> was created,
+ /// or null if none was supplied.
+ /// </summary>
+ public object AsyncState
+ {
+ get { return m_stateObject; }
+ }
+
+ /// <summary>
+ /// Gets an indication of whether the asynchronous operation completed synchronously.
+ /// </summary>
+ /// <value>true if the asynchronous operation completed synchronously; otherwise, false.</value>
+ bool IAsyncResult.CompletedSynchronously
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Provides access to the TaskScheduler responsible for executing this Task.
+ /// </summary>
+ internal TaskScheduler ExecutingTaskScheduler
+ {
+ get { return m_taskScheduler; }
+ }
+
+ /// <summary>
+ /// Provides access to factory methods for creating <see cref="Task"/> and <see cref="Task{TResult}"/> instances.
+ /// </summary>
+ /// <remarks>
+ /// The factory returned from <see cref="Factory"/> is a default instance
+ /// of <see cref="System.Threading.Tasks.TaskFactory"/>, as would result from using
+ /// the default constructor on TaskFactory.
+ /// </remarks>
+ public static TaskFactory Factory { get { return s_factory; } }
+
+ /// <summary>A task that's already been completed successfully.</summary>
+ private static Task s_completedTask;
+
+ /// <summary>Gets a task that's already been completed successfully.</summary>
+ /// <remarks>May not always return the same instance.</remarks>
+ public static Task CompletedTask
+ {
+ get
+ {
+ var completedTask = s_completedTask;
+ if (completedTask == null)
+ s_completedTask = completedTask = new Task(false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); // benign initialization race condition
+ return completedTask;
+ }
+ }
+
+ /// <summary>
+ /// Provides an event that can be used to wait for completion.
+ /// Only called by IAsyncResult.AsyncWaitHandle, which means that we really do need to instantiate a completion event.
+ /// </summary>
+ internal ManualResetEventSlim CompletedEvent
+ {
+ get
+ {
+ var contingentProps = EnsureContingentPropertiesInitialized();
+ if (contingentProps.m_completionEvent == null)
+ {
+ bool wasCompleted = IsCompleted;
+ ManualResetEventSlim newMre = new ManualResetEventSlim(wasCompleted);
+ if (Interlocked.CompareExchange(ref contingentProps.m_completionEvent, newMre, null) != null)
+ {
+ // Someone else already set the value, so we will just close the event right away.
+ newMre.Dispose();
+ }
+ else if (!wasCompleted && IsCompleted)
+ {
+ // We published the event as unset, but the task has subsequently completed.
+ // Set the event's state properly so that callers don't deadlock.
+ newMre.Set();
+ }
+ }
+
+ return contingentProps.m_completionEvent;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether this is the root task of a self replicating group.
+ /// </summary>
+ internal bool IsSelfReplicatingRoot
+ {
+ get
+ {
+ // Return true if self-replicating bit is set and child replica bit is not set
+ return (Options & (TaskCreationOptions)(InternalTaskOptions.SelfReplicating | InternalTaskOptions.ChildReplica))
+ == (TaskCreationOptions)InternalTaskOptions.SelfReplicating;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether the task is a replica itself.
+ /// </summary>
+ internal bool IsChildReplica
+ {
+ get { return (Options & (TaskCreationOptions)InternalTaskOptions.ChildReplica) != 0; }
+ }
+
+ internal int ActiveChildCount
+ {
+ get
+ {
+ var props = Volatile.Read(ref m_contingentProperties);
+ return props != null ? props.m_completionCountdown - 1 : 0;
+ }
+ }
+
+ /// <summary>
+ /// The property formerly known as IsFaulted.
+ /// </summary>
+ internal bool ExceptionRecorded
+ {
+ get
+ {
+ var props = Volatile.Read(ref m_contingentProperties);
+ return (props != null) && (props.m_exceptionsHolder != null) && (props.m_exceptionsHolder.ContainsFaultList);
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the <see cref="Task"/> completed due to an unhandled exception.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="IsFaulted"/> is true, the Task's <see cref="Status"/> will be equal to
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">TaskStatus.Faulted</see>, and its
+ /// <see cref="Exception"/> property will be non-null.
+ /// </remarks>
+ public bool IsFaulted
+ {
+ get
+ {
+ // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted.
+ return ((m_stateFlags & TASK_STATE_FAULTED) != 0);
+ }
+ }
+
+ /// <summary>
+ /// The captured execution context for the current task to run inside
+ /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise
+ /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context
+ /// </summary>
+ internal ExecutionContext CapturedContext
+ {
+ get
+ {
+ if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL)
+ {
+ return null;
+ }
+ else
+ {
+ return m_contingentProperties?.m_capturedContext ?? ExecutionContext.PreAllocatedDefault;
+ }
+ }
+ set
+ {
+ // There is no need to atomically set this bit because this set() method is only called during construction, and therefore there should be no contending accesses to m_stateFlags
+ if (value == null)
+ {
+ m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL;
+ }
+ else if (!value.IsPreAllocatedDefault) // not the default context, then inflate the contingent properties and set it
+ {
+ EnsureContingentPropertiesInitializedUnsafe().m_capturedContext = value;
+ }
+ //else do nothing, this is the default context
+ }
+ }
+
+ /// <summary>
+ /// Static helper function to copy specific ExecutionContext
+ /// </summary>
+ /// <param name="capturedContext">The captured context</param>
+ /// <returns>The copied context, null if the capturedContext is null</returns>
+ private static ExecutionContext CopyExecutionContext(ExecutionContext capturedContext)
+ {
+ if (capturedContext == null)
+ return null;
+ if (capturedContext.IsPreAllocatedDefault)
+ return ExecutionContext.PreAllocatedDefault;
+
+ return capturedContext.CreateCopy();
+ }
+
+
+#if DEBUG
+ /// <summary>
+ /// Retrieves an identifier for the task.
+ /// </summary>
+ internal int InternalId
+ {
+ get { return GetHashCode(); }
+ }
+#endif
+
+ /////////////
+ // methods
+
+
+ /// <summary>
+ /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
+ /// Also, <see cref="Dispose()"/> may only be called on a <see cref="Task"/> that is in one of
+ /// the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The exception that is thrown if the <see cref="Task"/> is not in
+ /// one of the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </exception>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
+ /// </summary>
+ /// <param name="disposing">
+ /// A Boolean value that indicates whether this method is being called due to a call to <see
+ /// cref="Dispose()"/>.
+ /// </param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Dispose is a nop if this task was created with the DoNotDispose internal option.
+ // This is done before the completed check, because if we're not touching any
+ // state on the task, it's ok for it to happen before completion.
+ if ((Options & (TaskCreationOptions)InternalTaskOptions.DoNotDispose) != 0)
+ {
+ return;
+ }
+
+ // Task must be completed to dispose
+ if (!IsCompleted)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Dispose_NotCompleted);
+ }
+
+ // Dispose of the underlying completion event if it exists
+ var cp = Volatile.Read(ref m_contingentProperties);
+ if (cp != null)
+ {
+ // Make a copy to protect against racing Disposes.
+ // If we wanted to make this a bit safer, we could use an interlocked here,
+ // but we state that Dispose is not thread safe.
+ var ev = cp.m_completionEvent;
+ if (ev != null)
+ {
+ // Null out the completion event in contingent props; we'll use our copy from here on out
+ cp.m_completionEvent = null;
+
+ // In the unlikely event that our completion event is inflated but not yet signaled,
+ // go ahead and signal the event. If you dispose of an unsignaled MRES, then any waiters
+ // will deadlock; an ensuing Set() will not wake them up. In the event of an AppDomainUnload,
+ // there is no guarantee that anyone else is going to signal the event, and it does no harm to
+ // call Set() twice on m_completionEvent.
+ if (!ev.IsSet) ev.Set();
+
+ // Finally, dispose of the event
+ ev.Dispose();
+ }
+ }
+ }
+
+ // We OR the flags to indicate the object has been disposed. The task
+ // has already completed at this point, and the only conceivable race condition would
+ // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which
+ // is extremely unlikely and also benign. (Worst case: we hit a breakpoint
+ // twice instead of once in the debugger. Weird, but not lethal.)
+ m_stateFlags |= TASK_STATE_DISPOSED;
+ }
+
+ /////////////
+ // internal helpers
+
+
+ /// <summary>
+ /// Schedules the task for execution.
+ /// </summary>
+ /// <param name="needsProtection">If true, TASK_STATE_STARTED bit is turned on in
+ /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set
+ /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This
+ /// allows us to streamline things a bit for StartNew(), where competing cancellations
+ /// are not a problem.</param>
+ [SecuritySafeCritical] // Needed for QueueTask
+ internal void ScheduleAndStart(bool needsProtection)
+ {
+ Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
+ Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");
+
+ // Set the TASK_STATE_STARTED bit
+ if (needsProtection)
+ {
+ if (!MarkStarted())
+ {
+ // A cancel has snuck in before we could get started. Quietly exit.
+ return;
+ }
+ }
+ else
+ {
+ m_stateFlags |= TASK_STATE_STARTED;
+ }
+
+ if (s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+
+ if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
+ {
+ //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, 0);
+ }
+
+
+ try
+ {
+ // Queue to the indicated scheduler.
+ m_taskScheduler.InternalQueueTask(this);
+ }
+ catch (ThreadAbortException tae)
+ {
+ AddException(tae);
+ FinishThreadAbortedTask(true, false);
+ }
+ catch (Exception e)
+ {
+ // The scheduler had a problem queueing this task. Record the exception, leaving this task in
+ // a Faulted state.
+ TaskSchedulerException tse = new TaskSchedulerException(e);
+ AddException(tse);
+ Finish(false);
+
+ // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
+ // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
+ // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
+ // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.
+
+ if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
+ {
+ // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
+ Contract.Assert(
+ (m_contingentProperties != null) &&
+ (m_contingentProperties.m_exceptionsHolder != null) &&
+ (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
+ "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
+ "and to have faults recorded.");
+
+ m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
+ }
+ // re-throw the exception wrapped as a TaskSchedulerException.
+ throw tse;
+ }
+ }
+
+ /// <summary>
+ /// Adds an exception to the list of exceptions this task has thrown.
+ /// </summary>
+ /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
+ internal void AddException(object exceptionObject)
+ {
+ Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
+ AddException(exceptionObject, representsCancellation: false);
+ }
+
+ /// <summary>
+ /// Adds an exception to the list of exceptions this task has thrown.
+ /// </summary>
+ /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
+ /// <param name="representsCancellation">Whether the exceptionObject is an OperationCanceledException representing cancellation.</param>
+ internal void AddException(object exceptionObject, bool representsCancellation)
+ {
+ Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
+
+#if DEBUG
+ var eoAsException = exceptionObject as Exception;
+ var eoAsEnumerableException = exceptionObject as IEnumerable<Exception>;
+ var eoAsEdi = exceptionObject as ExceptionDispatchInfo;
+ var eoAsEnumerableEdi = exceptionObject as IEnumerable<ExceptionDispatchInfo>;
+
+ Contract.Assert(
+ eoAsException != null || eoAsEnumerableException != null || eoAsEdi != null || eoAsEnumerableEdi != null,
+ "Task.AddException: Expected an Exception, ExceptionDispatchInfo, or an IEnumerable<> of one of those");
+
+ var eoAsOce = exceptionObject as OperationCanceledException;
+
+ Contract.Assert(
+ !representsCancellation ||
+ eoAsOce != null ||
+ (eoAsEdi != null && eoAsEdi.SourceException is OperationCanceledException),
+ "representsCancellation should be true only if an OCE was provided.");
+#endif
+
+ //
+ // WARNING: A great deal of care went into ensuring that
+ // AddException() and GetExceptions() are never called
+ // simultaneously. See comment at start of GetExceptions().
+ //
+
+ // Lazily initialize the holder, ensuring only one thread wins.
+ var props = EnsureContingentPropertiesInitialized();
+ if (props.m_exceptionsHolder == null)
+ {
+ TaskExceptionHolder holder = new TaskExceptionHolder(this);
+ if (Interlocked.CompareExchange(ref props.m_exceptionsHolder, holder, null) != null)
+ {
+ // If someone else already set the value, suppress finalization.
+ holder.MarkAsHandled(false);
+ }
+ }
+
+ lock (props)
+ {
+ props.m_exceptionsHolder.Add(exceptionObject, representsCancellation);
+ }
+ }
+
+ /// <summary>
+ /// Returns a list of exceptions by aggregating the holder's contents. Or null if
+ /// no exceptions have been thrown.
+ /// </summary>
+ /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param>
+ /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns>
+ private AggregateException GetExceptions(bool includeTaskCanceledExceptions)
+ {
+ //
+ // WARNING: The Task/Task<TResult>/TaskCompletionSource classes
+ // have all been carefully crafted to insure that GetExceptions()
+ // is never called while AddException() is being called. There
+ // are locks taken on m_contingentProperties in several places:
+ //
+ // -- Task<TResult>.TrySetException(): The lock allows the
+ // task to be set to Faulted state, and all exceptions to
+ // be recorded, in one atomic action.
+ //
+ // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException()
+ // is allowed to complete its operation before Task.Exception_get()
+ // can access GetExceptions().
+ //
+ // -- Task.ThrowIfExceptional(): The lock insures that Wait() will
+ // not attempt to call GetExceptions() while Task<TResult>.TrySetException()
+ // is in the process of calling AddException().
+ //
+ // For "regular" tasks, we effectively keep AddException() and GetException()
+ // from being called concurrently by the way that the state flows. Until
+ // a Task is marked Faulted, Task.Exception_get() returns null. And
+ // a Task is not marked Faulted until it and all of its children have
+ // completed, which means that all exceptions have been recorded.
+ //
+ // It might be a lot easier to follow all of this if we just required
+ // that all calls to GetExceptions() and AddExceptions() were made
+ // under a lock on m_contingentProperties. But that would also
+ // increase our lock occupancy time and the frequency with which we
+ // would need to take the lock.
+ //
+ // If you add a call to GetExceptions() anywhere in the code,
+ // please continue to maintain the invariant that it can't be
+ // called when AddException() is being called.
+ //
+
+ // We'll lazily create a TCE if the task has been canceled.
+ Exception canceledException = null;
+ if (includeTaskCanceledExceptions && IsCanceled)
+ {
+ // Backcompat:
+ // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo()
+ // here. However, that would result in a potentially breaking change from .NET 4, which
+ // has the code here that throws a new exception instead of the original, and the EDI
+ // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be
+ // propagating an exception of a different type.
+ canceledException = new TaskCanceledException(this);
+ }
+
+ if (ExceptionRecorded)
+ {
+ // There are exceptions; get the aggregate and optionally add the canceled
+ // exception to the aggregate (if applicable).
+ Contract.Assert(m_contingentProperties != null); // ExceptionRecorded ==> m_contingentProperties != null
+
+ // No need to lock around this, as other logic prevents the consumption of exceptions
+ // before they have been completely processed.
+ return m_contingentProperties.m_exceptionsHolder.CreateExceptionObject(false, canceledException);
+ }
+ else if (canceledException != null)
+ {
+ // No exceptions, but there was a cancelation. Aggregate and return it.
+ return new AggregateException(canceledException);
+ }
+
+ return null;
+ }
+
+ /// <summary>Gets the exception dispatch infos once the task has faulted.</summary>
+ internal ReadOnlyCollection<ExceptionDispatchInfo> GetExceptionDispatchInfos()
+ {
+ bool exceptionsAvailable = IsFaulted && ExceptionRecorded;
+ Contract.Assert(exceptionsAvailable, "Must only be used when the task has faulted with exceptions.");
+ return exceptionsAvailable ?
+ m_contingentProperties.m_exceptionsHolder.GetExceptionDispatchInfos() :
+ new ReadOnlyCollection<ExceptionDispatchInfo>(new ExceptionDispatchInfo[0]);
+ }
+
+ /// <summary>Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task.</summary>
+ /// <returns>The ExceptionDispatchInfo. May be null if no OCE was stored for the task.</returns>
+ internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo()
+ {
+ Contract.Assert(IsCanceled, "Must only be used when the task has canceled.");
+ return Volatile.Read(ref m_contingentProperties)?.m_exceptionsHolder?.GetCancellationExceptionDispatchInfo(); // may be null
+ }
+
+ /// <summary>
+ /// Throws an aggregate exception if the task contains exceptions.
+ /// </summary>
+ internal void ThrowIfExceptional(bool includeTaskCanceledExceptions)
+ {
+ Contract.Requires(IsCompleted, "ThrowIfExceptional(): Expected IsCompleted == true");
+
+ Exception exception = GetExceptions(includeTaskCanceledExceptions);
+ if (exception != null)
+ {
+ UpdateExceptionObservedStatus();
+ throw exception;
+ }
+ }
+
+ /// <summary>
+ /// Checks whether this is an attached task, and whether we are being called by the parent task.
+ /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that.
+ ///
+ /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering
+ /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children
+ /// will skip exceptions to prevent duplication.
+ ///
+ /// This should only be called when this task has completed with an exception
+ ///
+ /// </summary>
+ internal void UpdateExceptionObservedStatus()
+ {
+ Task parent = m_contingentProperties?.m_parent;
+ if ((parent != null)
+ && ((Options & TaskCreationOptions.AttachedToParent) != 0)
+ && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
+ && Task.InternalCurrent == parent)
+ {
+ m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT;
+ }
+ }
+
+ /// <summary>
+ /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set,
+ /// This will only be used by the implicit wait to prevent double throws
+ ///
+ /// </summary>
+ internal bool IsExceptionObservedByParent
+ {
+ get
+ {
+ return (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task.
+ /// </summary>
+ internal bool IsDelegateInvoked
+ {
+ get
+ {
+ return (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Signals completion of this particular task.
+ ///
+ /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the
+ /// full execution of the user delegate.
+ ///
+ /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to
+ /// a cancellation request, or because this task is a promise style Task). In this case, the steps
+ /// involving child tasks (i.e. WaitForChildren) will be skipped.
+ ///
+ /// </summary>
+ internal void Finish(bool bUserDelegateExecuted)
+ {
+ if (!bUserDelegateExecuted)
+ {
+ // delegate didn't execute => no children. We can safely call the remaining finish stages
+ FinishStageTwo();
+ }
+ else
+ {
+ var props = Volatile.Read(ref m_contingentProperties);
+
+ if (props == null || // no contingent properties means no children, so it's safe to complete ourselves
+ (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot) ||
+ // Count of 1 => either all children finished, or there were none. Safe to complete ourselves
+ // without paying the price of an Interlocked.Decrement.
+ // However we need to exclude self replicating root tasks from this optimization, because
+ // they can have children joining in, or finishing even after the root task delegate is done.
+ Interlocked.Decrement(ref props.m_completionCountdown) == 0) // Reaching this sub clause means there may be remaining active children,
+ // and we could be racing with one of them to call FinishStageTwo().
+ // So whoever does the final Interlocked.Dec is responsible to finish.
+ {
+ FinishStageTwo();
+ }
+ else
+ {
+ // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread.
+ // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out.
+
+ // We have to use an atomic update for this and make sure not to overwrite a final state,
+ // because at this very moment the last child's thread may be concurrently completing us.
+ // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task.
+ // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag,
+ // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo
+
+ AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION);
+ }
+
+ // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw.
+ // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren
+ List<Task> exceptionalChildren = props != null ? props.m_exceptionalChildren : null;
+
+ if (exceptionalChildren != null)
+ {
+ lock (exceptionalChildren)
+ {
+ exceptionalChildren.RemoveAll(s_IsExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves
+ }
+ }
+ }
+ }
+
+ // statically allocated delegate for the removeall expression in Finish()
+ private readonly static Predicate<Task> s_IsExceptionObservedByParentPredicate = new Predicate<Task>((t) => { return t.IsExceptionObservedByParent; });
+
+ /// <summary>
+ /// FinishStageTwo is to be executed as soon as we known there are no more children to complete.
+ /// It can happen i) either on the thread that originally executed this task (if no children were spawned, or they all completed by the time this task's delegate quit)
+ /// ii) or on the thread that executed the last child.
+ /// </summary>
+ internal void FinishStageTwo()
+ {
+ AddExceptionsFromChildren();
+
+ // At this point, the task is done executing and waiting for its children,
+ // we can transition our task to a completion state.
+ int completionState;
+ if (ExceptionRecorded)
+ {
+ completionState = TASK_STATE_FAULTED;
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Error);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ }
+ else if (IsCancellationRequested && IsCancellationAcknowledged)
+ {
+ // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation,
+ // and the user delegate acknowledged the cancellation request by throwing an OCE,
+ // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED)
+ //
+ // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state),
+ // then we regard it as a regular exception
+
+ completionState = TASK_STATE_CANCELED;
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ }
+ else
+ {
+ completionState = TASK_STATE_RAN_TO_COMPLETION;
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ }
+
+ // Use Interlocked.Exchange() to effect a memory fence, preventing
+ // any SetCompleted() (or later) instructions from sneak back before it.
+ Interlocked.Exchange(ref m_stateFlags, m_stateFlags | completionState);
+
+ // Set the completion event if it's been lazy allocated.
+ // And if we made a cancellation registration, it's now unnecessary.
+ var cp = Volatile.Read(ref m_contingentProperties);
+ if (cp != null)
+ {
+ cp.SetCompleted();
+ cp.DeregisterCancellationCallback();
+ }
+
+ // ready to run continuations and notify parent.
+ FinishStageThree();
+ }
+
+
+ /// <summary>
+ /// Final stage of the task completion code path. Notifies the parent (if any) that another of its children are done, and runs continuations.
+ /// This function is only separated out from FinishStageTwo because these two operations are also needed to be called from CancellationCleanupLogic()
+ /// </summary>
+ internal void FinishStageThree()
+ {
+ // Release the action so that holding this task object alive doesn't also
+ // hold alive the body of the task. We do this before notifying a parent,
+ // so that if notifying the parent completes the parent and causes
+ // its synchronous continuations to run, the GC can collect the state
+ // in the interim. And we do it before finishing continuations, because
+ // continuations hold onto the task, and therefore are keeping it alive.
+ m_action = null;
+
+ // Notify parent if this was an attached task
+ Task parent = m_contingentProperties?.m_parent;
+ if (parent != null
+ && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
+ && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0)
+ {
+ parent.ProcessChildCompletion(this);
+ }
+
+ // Activate continuations (if any).
+ FinishContinuations();
+ }
+
+ /// <summary>
+ /// This is called by children of this task when they are completed.
+ /// </summary>
+ internal void ProcessChildCompletion(Task childTask)
+ {
+ Contract.Requires(childTask != null);
+ Contract.Requires(childTask.IsCompleted, "ProcessChildCompletion was called for an uncompleted task");
+
+ Contract.Assert(childTask.m_contingentProperties?.m_parent == this, "ProcessChildCompletion should only be called for a child of this task");
+
+ var props = Volatile.Read(ref m_contingentProperties);
+
+ // if the child threw and we haven't observed it we need to save it for future reference
+ if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent)
+ {
+ // Lazily initialize the child exception list
+ if (props.m_exceptionalChildren == null)
+ {
+ Interlocked.CompareExchange(ref props.m_exceptionalChildren, new List<Task>(), null);
+ }
+
+ // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called
+ // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren
+ // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect
+ // ourselves by caching m_exceptionChildren in a local variable.
+ List<Task> tmp = props.m_exceptionalChildren;
+ if (tmp != null)
+ {
+ lock (tmp)
+ {
+ tmp.Add(childTask);
+ }
+ }
+
+ }
+
+ if (Interlocked.Decrement(ref props.m_completionCountdown) == 0)
+ {
+ // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself.
+ // So we need to invoke the final finish stage.
+
+ FinishStageTwo();
+ }
+ }
+
+ /// <summary>
+ /// This is to be called just before the task does its final state transition.
+ /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list
+ /// </summary>
+ internal void AddExceptionsFromChildren()
+ {
+ // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called
+ // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren
+ // being nulled out while it is being processed, which could lead to a NullReferenceException. To
+ // protect ourselves, we'll cache m_exceptionalChildren in a local variable.
+ var props = Volatile.Read(ref m_contingentProperties);
+ List<Task> exceptionalChildren = props?.m_exceptionalChildren;
+
+ if (exceptionalChildren != null)
+ {
+ // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still
+ // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array.
+ lock (exceptionalChildren)
+ {
+ foreach (Task task in exceptionalChildren)
+ {
+ // Ensure any exceptions thrown by children are added to the parent.
+ // In doing this, we are implicitly marking children as being "handled".
+ Contract.Assert(task.IsCompleted, "Expected all tasks in list to be completed");
+ if (task.IsFaulted && !task.IsExceptionObservedByParent)
+ {
+ TaskExceptionHolder exceptionHolder = Volatile.Read(ref task.m_contingentProperties).m_exceptionsHolder;
+ Contract.Assert(exceptionHolder != null);
+
+ // No locking necessary since child task is finished adding exceptions
+ // and concurrent CreateExceptionObject() calls do not constitute
+ // a concurrency hazard.
+ AddException(exceptionHolder.CreateExceptionObject(false, null));
+ }
+ }
+ }
+
+ // Reduce memory pressure by getting rid of the array
+ props.m_exceptionalChildren = null;
+ }
+ }
+
+ /// <summary>
+ /// Special purpose Finish() entry point to be used when the task delegate throws a ThreadAbortedException
+ /// This makes a note in the state flags so that we avoid any costly synchronous operations in the finish codepath
+ /// such as inlined continuations
+ /// </summary>
+ /// <param name="bTAEAddedToExceptionHolder">
+ /// Indicates whether the ThreadAbortException was added to this task's exception holder.
+ /// This should always be true except for the case of non-root self replicating task copies.
+ /// </param>
+ /// <param name="delegateRan">Whether the delegate was executed.</param>
+ internal void FinishThreadAbortedTask(bool bTAEAddedToExceptionHolder, bool delegateRan)
+ {
+ Contract.Assert(!bTAEAddedToExceptionHolder || m_contingentProperties?.m_exceptionsHolder != null,
+ "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized");
+
+ // this will only be false for non-root self replicating task copies, because all of their exceptions go to the root task.
+ if (bTAEAddedToExceptionHolder)
+ m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
+
+ // If this method has already been called for this task, or if this task has already completed, then
+ // return before actually calling Finish().
+ if (!AtomicStateUpdate(TASK_STATE_THREAD_WAS_ABORTED,
+ TASK_STATE_THREAD_WAS_ABORTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
+ {
+ return;
+ }
+
+ Finish(delegateRan);
+
+ }
+
+
+ /// <summary>
+ /// Executes the task. This method will only be called once, and handles bookeeping associated with
+ /// self-replicating tasks, in addition to performing necessary exception marshaling.
+ /// </summary>
+ private void Execute()
+ {
+ if (IsSelfReplicatingRoot)
+ {
+ ExecuteSelfReplicating(this);
+ }
+ else
+ {
+ try
+ {
+ InnerInvoke();
+ }
+ catch (ThreadAbortException tae)
+ {
+ // Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
+ // it's already been done downstream.
+ if (!IsChildReplica)
+ {
+ // Record this exception in the task's exception list
+ HandleException(tae);
+
+ // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
+ // skip the regular Finish codepath. In order not to leave the task unfinished, we now call
+ // FinishThreadAbortedTask here.
+ FinishThreadAbortedTask(true, true);
+ }
+ }
+ catch (Exception exn)
+ {
+ // Record this exception in the task's exception list
+ HandleException(exn);
+ }
+ }
+ }
+
+ // Allows (internal) deriving classes to support limited replication.
+ // (By default, replication is basically unlimited).
+ internal virtual bool ShouldReplicate()
+ {
+ return true;
+ }
+
+ // Allows (internal) deriving classes to instantiate the task replica as a Task super class of their choice
+ // (By default, we create a regular Task instance)
+ internal virtual Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
+ TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
+ {
+ return new Task(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken),
+ creationOptionsForReplica, internalOptionsForReplica, parentTask.ExecutingTaskScheduler);
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
+ internal virtual Object SavedStateForNextReplica
+ {
+ get { return null; }
+
+ set { /*do nothing*/ }
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
+ internal virtual Object SavedStateFromPreviousReplica
+ {
+ get { return null; }
+
+ set { /*do nothing*/ }
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
+ // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
+ internal virtual Task HandedOverChildReplica
+ {
+ get { return null; }
+
+ set { /* do nothing*/ }
+ }
+
+ private static void ExecuteSelfReplicating(Task root)
+ {
+ TaskCreationOptions creationOptionsForReplicas = root.CreationOptions | TaskCreationOptions.AttachedToParent;
+ InternalTaskOptions internalOptionsForReplicas =
+ InternalTaskOptions.ChildReplica | // child replica flag disables self replication for the replicas themselves.
+ InternalTaskOptions.SelfReplicating | // we still want to identify this as part of a self replicating group
+ InternalTaskOptions.QueuedByRuntime; // we queue and cancel these tasks internally, so don't allow CT registration to take place
+
+
+ // Important Note: The child replicas we launch from here will be attached the root replica (by virtue of the root.CreateReplicaTask call)
+ // because we need the root task to receive all their exceptions, and to block until all of them return
+
+
+ // This variable is captured in a closure and shared among all replicas.
+ bool replicasAreQuitting = false;
+
+ // Set up a delegate that will form the body of the root and all recursively created replicas.
+ Action<object> taskReplicaDelegate = null;
+ taskReplicaDelegate = delegate
+ {
+ Task currentTask = Task.InternalCurrent;
+
+
+ // Check if a child task has been handed over by a prematurely quiting replica that we might be a replacement for.
+ Task childTask = currentTask.HandedOverChildReplica;
+
+ if (childTask == null)
+ {
+ // Apparently we are not a replacement task. This means we need to queue up a child task for replication to progress
+
+ // Down-counts a counter in the root task.
+ if (!root.ShouldReplicate()) return;
+
+ // If any of the replicas have quit, we will do so ourselves.
+ if (Volatile.Read(ref replicasAreQuitting))
+ {
+ return;
+ }
+
+ // Propagate a copy of the context from the root task. It may be null if flow was suppressed.
+ ExecutionContext creatorContext = root.CapturedContext;
+
+
+ childTask = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
+ creationOptionsForReplicas, internalOptionsForReplicas);
+
+ childTask.CapturedContext = CopyExecutionContext(creatorContext);
+
+ childTask.ScheduleAndStart(false);
+ }
+
+
+
+ // Finally invoke the meat of the task.
+ // Note that we are directly calling root.InnerInvoke() even though we are currently be in the action delegate of a child replica
+ // This is because the actual work was passed down in that delegate, and the action delegate of the child replica simply contains this
+ // replication control logic.
+ try
+ {
+ // passing in currentTask only so that the parallel debugger can find it
+ root.InnerInvokeWithArg(currentTask);
+ }
+ catch (Exception exn)
+ {
+ // Record this exception in the root task's exception list
+ root.HandleException(exn);
+
+ if (exn is ThreadAbortException)
+ {
+ // If this is a ThreadAbortException it will escape this catch clause, causing us to skip the regular Finish codepath
+ // In order not to leave the task unfinished, we now call FinishThreadAbortedTask here
+ currentTask.FinishThreadAbortedTask(false, true);
+ }
+ }
+
+ Object savedState = currentTask.SavedStateForNextReplica;
+
+ // check for premature exit
+ if (savedState != null)
+ {
+ // the replica decided to exit early
+ // we need to queue up a replacement, attach the saved state, and yield the thread right away
+
+ Task replacementReplica = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
+ creationOptionsForReplicas, internalOptionsForReplicas);
+
+ // Propagate a copy of the context from the root task to the replacement task
+ ExecutionContext creatorContext = root.CapturedContext;
+ replacementReplica.CapturedContext = CopyExecutionContext(creatorContext);
+
+ replacementReplica.HandedOverChildReplica = childTask;
+ replacementReplica.SavedStateFromPreviousReplica = savedState;
+
+ replacementReplica.ScheduleAndStart(false);
+ }
+ else
+ {
+ // The replica finished normally, which means it can't find more work to grab.
+ // Time to mark replicas quitting
+
+ replicasAreQuitting = true;
+
+ // InternalCancel() could conceivably throw in the underlying scheduler's TryDequeue() method.
+ // If it does, then make sure that we record it.
+ try
+ {
+ childTask.InternalCancel(true);
+ }
+ catch (Exception e)
+ {
+ // Apparently TryDequeue threw an exception. Before propagating that exception, InternalCancel should have
+ // attempted an atomic state transition and a call to CancellationCleanupLogic() on this task. So we know
+ // the task was properly cleaned up if it was possible.
+ //
+ // Now all we need to do is to Record the exception in the root task.
+
+ root.HandleException(e);
+ }
+
+ // No specific action needed if the child could not be canceled
+ // because we attached it to the root task, which should therefore be receiving any exceptions from the child,
+ // and root.wait will not return before this child finishes anyway.
+
+ }
+ };
+
+ //
+ // Now we execute as the root task
+ //
+ taskReplicaDelegate(null);
+ }
+
+ /// <summary>
+ /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it.
+ ///
+ /// </summary>
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+ ExecuteEntry(false);
+ }
+
+ /// <summary>
+ /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. This may occur
+ /// before Task would otherwise be able to observe it.
+ /// </summary>
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
+ {
+ // If the task has marked itself as Completed, then it either a) already observed this exception (so we shouldn't handle it here)
+ // or b) completed before the exception ocurred (in which case it shouldn't count against this Task).
+ if (!IsCompleted)
+ {
+ HandleException(tae);
+ FinishThreadAbortedTask(true, false);
+ }
+ }
+
+ /// <summary>
+ /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread.
+ /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline.
+ ///
+ /// </summary>
+ /// <param name="bPreventDoubleExecution"> Performs atomic updates to prevent double execution. Should only be set to true
+ /// in codepaths servicing user provided TaskSchedulers. The ConcRT or ThreadPool schedulers don't need this. </param>
+ [SecuritySafeCritical]
+ internal bool ExecuteEntry(bool bPreventDoubleExecution)
+ {
+ if (bPreventDoubleExecution || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0))
+ {
+ int previousState = 0;
+
+ // Do atomic state transition from queued to invoked. If we observe a task that's already invoked,
+ // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler.
+ // However we don't want this exception to be throw if the task was already canceled, because it's a
+ // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler)
+ if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED,
+ TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK,
+ ref previousState) && (previousState & TASK_STATE_CANCELED) == 0)
+ {
+ // This task has already been invoked. Don't invoke it again.
+ return false;
+ }
+ }
+ else
+ {
+ // Remember that we started running the task delegate.
+ m_stateFlags |= TASK_STATE_DELEGATE_INVOKED;
+ }
+
+ if (!IsCancellationRequested && !IsCanceled)
+ {
+ ExecuteWithThreadLocal(ref t_currentTask);
+ }
+ else if (!IsCanceled)
+ {
+ int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
+ if ((prevState & TASK_STATE_CANCELED) == 0)
+ {
+ CancellationCleanupLogic();
+ }
+ }
+
+ return true;
+ }
+
+ // A trick so we can refer to the TLS slot with a byref.
+ [SecurityCritical]
+ private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
+ {
+ // Remember the current task so we can restore it after running, and then
+ Task previousTask = currentTaskSlot;
+
+ // ETW event for Task Started
+ var etwLog = TplEtwProvider.Log;
+ Guid savedActivityID = new Guid();
+ bool etwIsEnabled = etwLog.IsEnabled();
+ if (etwIsEnabled)
+ {
+ if (etwLog.TasksSetActivityIds)
+ EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(this.Id), out savedActivityID);
+ // previousTask holds the actual "current task" we want to report in the event
+ if (previousTask != null)
+ etwLog.TaskStarted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id);
+ else
+ etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id);
+ }
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution);
+
+
+ try
+ {
+ // place the current task into TLS.
+ currentTaskSlot = this;
+
+ ExecutionContext ec = CapturedContext;
+ if (ec == null)
+ {
+ // No context, just run the task directly.
+ Execute();
+ }
+ else
+ {
+ if (IsSelfReplicatingRoot || IsChildReplica)
+ {
+ CapturedContext = CopyExecutionContext(ec);
+ }
+
+ // Run the task. We need a simple shim that converts the
+ // object back into a Task object, so that we can Execute it.
+
+ // Lazily initialize the callback delegate; benign race condition
+ var callback = s_ecCallback;
+ if (callback == null) s_ecCallback = callback = new ContextCallback(ExecutionContextCallback);
+ ExecutionContext.Run(ec, callback, this, true);
+ }
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
+
+ Finish(true);
+ }
+ finally
+ {
+ currentTaskSlot = previousTask;
+
+ // ETW event for Task Completed
+ if (etwIsEnabled)
+ {
+ // previousTask holds the actual "current task" we want to report in the event
+ if (previousTask != null)
+ etwLog.TaskCompleted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id, IsFaulted);
+ else
+ etwLog.TaskCompleted(TaskScheduler.Current.Id, 0, this.Id, IsFaulted);
+
+ if (etwLog.TasksSetActivityIds)
+ EventSource.SetCurrentThreadActivityId(savedActivityID);
+ }
+ }
+ }
+
+ // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical
+ [SecurityCritical]
+ private static ContextCallback s_ecCallback;
+
+ [SecurityCritical]
+ private static void ExecutionContextCallback(object obj)
+ {
+ Task task = obj as Task;
+ Contract.Assert(task != null, "expected a task object");
+ task.Execute();
+ }
+
+
+ /// <summary>
+ /// The actual code which invokes the body of the task. This can be overriden in derived types.
+ /// </summary>
+ internal virtual void InnerInvoke()
+ {
+ // Invoke the delegate
+ Contract.Assert(m_action != null, "Null action in InnerInvoke()");
+ var action = m_action as Action;
+ if (action != null)
+ {
+ action();
+ return;
+ }
+ var actionWithState = m_action as Action<object>;
+ if (actionWithState != null)
+ {
+ actionWithState(m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in Task");
+ }
+
+ /// <summary>
+ /// Alternate InnerInvoke prototype to be called from ExecuteSelfReplicating() so that
+ /// the Parallel Debugger can discover the actual task being invoked.
+ /// Details: Here, InnerInvoke is actually being called on the rootTask object while we are actually executing the
+ /// childTask. And the debugger needs to discover the childTask, so we pass that down as an argument.
+ /// The NoOptimization and NoInlining flags ensure that the childTask pointer is retained, and that this
+ /// function appears on the callstack.
+ /// </summary>
+ /// <param name="childTask"></param>
+ [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+ internal void InnerInvokeWithArg(Task childTask)
+ {
+ InnerInvoke();
+ }
+
+ /// <summary>
+ /// Performs whatever handling is necessary for an unhandled exception. Normally
+ /// this just entails adding the exception to the holder object.
+ /// </summary>
+ /// <param name="unhandledException">The exception that went unhandled.</param>
+ private void HandleException(Exception unhandledException)
+ {
+ Contract.Requires(unhandledException != null);
+
+ OperationCanceledException exceptionAsOce = unhandledException as OperationCanceledException;
+ if (exceptionAsOce != null && IsCancellationRequested &&
+ m_contingentProperties.m_cancellationToken == exceptionAsOce.CancellationToken)
+ {
+ // All conditions are satisfied for us to go into canceled state in Finish().
+ // Mark the acknowledgement. The exception is also stored to enable it to be
+ // the exception propagated from an await.
+
+ SetCancellationAcknowledged();
+ AddException(exceptionAsOce, representsCancellation: true);
+ }
+ else
+ {
+ // Other exceptions, including any OCE from the task that doesn't match the tasks' own CT,
+ // or that gets thrown without the CT being set will be treated as an ordinary exception
+ // and added to the aggregate.
+
+ AddException(unhandledException);
+ }
+ }
+
+ #region Await Support
+ /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
+ /// <returns>An awaiter instance.</returns>
+ /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
+ public TaskAwaiter GetAwaiter()
+ {
+ return new TaskAwaiter(this);
+ }
+
+ /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
+ /// </param>
+ /// <returns>An object used to await this task.</returns>
+ public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
+ {
+ return new ConfiguredTaskAwaitable(this, continueOnCapturedContext);
+ }
+
+ /// <summary>
+ /// Sets a continuation onto the <see cref="System.Threading.Tasks.Task"/>.
+ /// The continuation is scheduled to run in the current synchronization context is one exists,
+ /// otherwise in the current task scheduler.
+ /// </summary>
+ /// <param name="continuationAction">The action to invoke when the <see cref="System.Threading.Tasks.Task"/> has completed.</param>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
+ /// </param>
+ /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param>
+ /// <param name="stackMark">A stack crawl mark tied to execution context.</param>
+ /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
+ [SecurityCritical]
+ internal void SetContinuationForAwait(
+ Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
+ {
+ Contract.Requires(continuationAction != null);
+
+ // Create the best AwaitTaskContinuation object given the request.
+ // If this remains null by the end of the function, we can use the
+ // continuationAction directly without wrapping it.
+ TaskContinuation tc = null;
+
+ // If the user wants the continuation to run on the current "context" if there is one...
+ if (continueOnCapturedContext)
+ {
+ // First try getting the current synchronization context.
+ // If the current context is really just the base SynchronizationContext type,
+ // which is intended to be equivalent to not having a current SynchronizationContext at all,
+ // then ignore it. This helps with performance by avoiding unnecessary posts and queueing
+ // of work items, but more so it ensures that if code happens to publish the default context
+ // as current, it won't prevent usage of a current task scheduler if there is one.
+ var syncCtx = SynchronizationContext.CurrentNoFlow;
+ if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
+ {
+ tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
+ }
+ else
+ {
+ // If there was no SynchronizationContext, then try for the current scheduler.
+ // We only care about it if it's not the default.
+ var scheduler = TaskScheduler.InternalCurrent;
+ if (scheduler != null && scheduler != TaskScheduler.Default)
+ {
+ tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
+ }
+ }
+ }
+
+ if (tc == null && flowExecutionContext)
+ {
+ // We're targeting the default scheduler, so we can use the faster path
+ // that assumes the default, and thus we don't need to store it. If we're flowing
+ // ExecutionContext, we need to capture it and wrap it in an AwaitTaskContinuation.
+ // Otherwise, we're targeting the default scheduler and we don't need to flow ExecutionContext, so
+ // we don't actually need a continuation object. We can just store/queue the action itself.
+ tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true, stackMark: ref stackMark);
+ }
+
+ // Now register the continuation, and if we couldn't register it because the task is already completing,
+ // process the continuation directly (in which case make sure we schedule the continuation
+ // rather than inlining it, the latter of which could result in a rare but possible stack overflow).
+ if (tc != null)
+ {
+ if (!AddTaskContinuation(tc, addBeforeOthers: false))
+ tc.Run(this, bCanInlineContinuationTask: false);
+ }
+ else
+ {
+ Contract.Assert(!flowExecutionContext, "We already determined we're not required to flow context.");
+ if (!AddTaskContinuation(continuationAction, addBeforeOthers: false))
+ AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
+ }
+ }
+
+ /// <summary>Creates an awaitable that asynchronously yields back to the current context when awaited.</summary>
+ /// <returns>
+ /// A context that, when awaited, will asynchronously transition back into the current context at the
+ /// time of the await. If the current SynchronizationContext is non-null, that is treated as the current context.
+ /// Otherwise, TaskScheduler.Current is treated as the current context.
+ /// </returns>
+ public static YieldAwaitable Yield()
+ {
+ return new YieldAwaitable();
+ }
+ #endregion
+
+ /// <summary>
+ /// Waits for the <see cref="Task"/> to complete execution.
+ /// </summary>
+ /// <exception cref="T:System.AggregateException">
+ /// The <see cref="Task"/> was canceled -or- an exception was thrown during
+ /// the execution of the <see cref="Task"/>.
+ /// </exception>
+ public void Wait()
+ {
+#if DEBUG
+ bool waitResult =
+#endif
+ Wait(Timeout.Infinite, default(CancellationToken));
+
+#if DEBUG
+ Contract.Assert(waitResult, "expected wait to succeed");
+#endif
+ }
+
+ /// <summary>
+ /// Waits for the <see cref="Task"/> to complete execution.
+ /// </summary>
+ /// <param name="timeout">
+ /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
+ /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <returns>
+ /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.AggregateException">
+ /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
+ /// cref="Task"/>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
+ /// infinite time-out -or- timeout is greater than
+ /// <see cref="System.Int32.MaxValue"/>.
+ /// </exception>
+ public bool Wait(TimeSpan timeout)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
+ }
+
+ return Wait((int)totalMilliseconds, default(CancellationToken));
+ }
+
+
+ /// <summary>
+ /// Waits for the <see cref="Task"/> to complete execution.
+ /// </summary>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
+ /// </param>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
+ /// cref="Task"/>.
+ /// </exception>
+ public void Wait(CancellationToken cancellationToken)
+ {
+ Wait(Timeout.Infinite, cancellationToken);
+ }
+
+
+ /// <summary>
+ /// Waits for the <see cref="Task"/> to complete execution.
+ /// </summary>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.</param>
+ /// <returns>true if the <see cref="Task"/> completed execution within the allotted time; otherwise,
+ /// false.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
+ /// cref="Task"/>.
+ /// </exception>
+ public bool Wait(int millisecondsTimeout)
+ {
+ return Wait(millisecondsTimeout, default(CancellationToken));
+ }
+
+
+ /// <summary>
+ /// Waits for the <see cref="Task"/> to complete execution.
+ /// </summary>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
+ /// </param>
+ /// <returns>
+ /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.AggregateException">
+ /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
+ /// cref="Task"/>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ if (millisecondsTimeout < -1)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
+ }
+ Contract.EndContractBlock();
+
+ // Return immediately if we know that we've completed "clean" -- no exceptions, no cancellations
+ // and if no notification to the debugger is required
+ if (!IsWaitNotificationEnabledOrNotRanToCompletion) // (!DebuggerBitSet && RanToCompletion)
+ return true;
+
+ // Wait, and then return if we're still not done.
+ if (!InternalWait(millisecondsTimeout, cancellationToken))
+ return false;
+
+ if (IsWaitNotificationEnabledOrNotRanToCompletion) // avoid a few unnecessary volatile reads if we completed successfully
+ {
+ // Notify the debugger of the wait completion if it's requested such a notification
+ NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // If cancellation was requested and the task was canceled, throw an
+ // OperationCanceledException. This is prioritized ahead of the ThrowIfExceptional
+ // call to bring more determinism to cases where the same token is used to
+ // cancel the Wait and to cancel the Task. Otherwise, there's a race condition between
+ // whether the Wait or the Task observes the cancellation request first,
+ // and different exceptions result from the different cases.
+ if (IsCanceled) cancellationToken.ThrowIfCancellationRequested();
+
+ // If an exception occurred, or the task was cancelled, throw an exception.
+ ThrowIfExceptional(true);
+ }
+
+ Contract.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state.");
+
+ return true;
+ }
+
+ // Convenience method that wraps any scheduler exception in a TaskSchedulerException
+ // and rethrows it.
+ private bool WrappedTryRunInline()
+ {
+ if (m_taskScheduler == null)
+ return false;
+
+ try
+ {
+ return m_taskScheduler.TryRunInline(this, true);
+ }
+ catch (Exception e)
+ {
+ // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
+ // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
+ if (!(e is ThreadAbortException))
+ {
+ TaskSchedulerException tse = new TaskSchedulerException(e);
+ throw tse;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// The core wait function, which is only accesible internally. It's meant to be used in places in TPL code where
+ /// the current context is known or cached.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ // ETW event for Task Wait Begin
+ var etwLog = TplEtwProvider.Log;
+ bool etwIsEnabled = etwLog.IsEnabled();
+ if (etwIsEnabled)
+ {
+ Task currentTask = Task.InternalCurrent;
+ etwLog.TaskWaitBegin(
+ (currentTask != null ? currentTask.m_taskScheduler.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0),
+ this.Id, TplEtwProvider.TaskWaitBehavior.Synchronous, 0, System.Threading.Thread.GetDomainID());
+ }
+
+ bool returnValue = IsCompleted;
+
+ // If the event hasn't already been set, we will wait.
+ if (!returnValue)
+ {
+ // Alert a listening debugger that we can't make forward progress unless it slips threads.
+ // We call NOCTD for two reasons:
+ // 1. If the task runs on another thread, then we'll be blocked here indefinitely.
+ // 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption,
+ // and it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
+ Debugger.NotifyOfCrossThreadDependency();
+
+ // We will attempt inline execution only if an infinite wait was requested
+ // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
+ // because we don't know how long the task delegate will take.
+ if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
+ WrappedTryRunInline() && IsCompleted) // TryRunInline doesn't guarantee completion, as there may be unfinished children.
+ {
+ returnValue = true;
+ }
+ else
+ {
+ returnValue = SpinThenBlockingWait(millisecondsTimeout, cancellationToken);
+ }
+ }
+
+ Contract.Assert(IsCompleted || millisecondsTimeout != Timeout.Infinite);
+
+ // ETW event for Task Wait End
+ if (etwIsEnabled)
+ {
+ Task currentTask = Task.InternalCurrent;
+ if (currentTask != null)
+ {
+ etwLog.TaskWaitEnd(currentTask.m_taskScheduler.Id, currentTask.Id, this.Id);
+ }
+ else
+ {
+ etwLog.TaskWaitEnd(TaskScheduler.Default.Id, 0, this.Id);
+ }
+ // logically the continuation is empty so we immediately fire
+ etwLog.TaskWaitContinuationComplete(this.Id);
+ }
+
+ return returnValue;
+ }
+
+ // An MRES that gets set when Invoke is called. This replaces old logic that looked like this:
+ // ManualResetEventSlim mres = new ManualResetEventSlim(false, 0);
+ // Action<Task> completionAction = delegate {mres.Set();}
+ // AddCompletionAction(completionAction);
+ // with this:
+ // SetOnInvokeMres mres = new SetOnInvokeMres();
+ // AddCompletionAction(mres, addBeforeOthers: true);
+ // which saves a couple of allocations.
+ //
+ // Used in SpinThenBlockingWait (below), but could be seen as a general purpose mechanism.
+ private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
+ {
+ internal SetOnInvokeMres() : base(false, 0) { }
+ public void Invoke(Task completingTask) { Set(); }
+ public bool InvokeMayRunArbitraryCode { get { return false; } }
+ }
+
+ /// <summary>
+ /// Waits for the task to complete, for a timeout to occur, or for cancellation to be requested.
+ /// The method first spins and then falls back to blocking on a new event.
+ /// </summary>
+ /// <param name="millisecondsTimeout">The timeout.</param>
+ /// <param name="cancellationToken">The token.</param>
+ /// <returns>true if the task is completed; otherwise, false.</returns>
+ private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ bool infiniteWait = millisecondsTimeout == Timeout.Infinite;
+ uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount;
+ bool returnValue = SpinWait(millisecondsTimeout);
+ if (!returnValue)
+ {
+ var mres = new SetOnInvokeMres();
+ try
+ {
+ AddCompletionAction(mres, addBeforeOthers: true);
+ if (infiniteWait)
+ {
+ returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
+ }
+ else
+ {
+ uint elapsedTimeTicks = ((uint)Environment.TickCount) - startTimeTicks;
+ if (elapsedTimeTicks < millisecondsTimeout)
+ {
+ returnValue = mres.Wait((int)(millisecondsTimeout - elapsedTimeTicks), cancellationToken);
+ }
+ }
+ }
+ finally
+ {
+ if (!IsCompleted) RemoveContinuation(mres);
+ // Don't Dispose of the MRES, because the continuation off of this task may
+ // still be running. This is ok, however, as we never access the MRES' WaitHandle,
+ // and thus no finalizable resources are actually allocated.
+ }
+ }
+ return returnValue;
+ }
+
+ /// <summary>
+ /// Spins briefly while checking IsCompleted
+ /// </summary>
+ /// <param name="millisecondsTimeout">The timeout.</param>
+ /// <returns>true if the task is completed; otherwise, false.</returns>
+ /// <exception cref="System.OperationCanceledException">The wait was canceled.</exception>
+ private bool SpinWait(int millisecondsTimeout)
+ {
+ if (IsCompleted) return true;
+
+ if (millisecondsTimeout == 0)
+ {
+ // For 0-timeouts, we just return immediately.
+ return false;
+ }
+
+ //This code is pretty similar to the custom spinning in MRES except there is no yieling after we exceed the spin count
+ int spinCount = PlatformHelper.IsSingleProcessor ? 1 : System.Threading.SpinWait.YIELD_THRESHOLD; //spin only once if we are running on a single CPU
+ for (int i = 0; i < spinCount; i++)
+ {
+ if (IsCompleted)
+ {
+ return true;
+ }
+
+ if (i == spinCount / 2)
+ {
+ Thread.Yield();
+ }
+ else
+ {
+ Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
+ }
+
+ }
+
+ return IsCompleted;
+ }
+
+ /// <summary>
+ /// Cancels the <see cref="Task"/>.
+ /// </summary>
+ /// <param name="bCancelNonExecutingOnly">
+ /// Indicates whether we should only cancel non-invoked tasks.
+ /// For the default scheduler this option will only be serviced through TryDequeue.
+ /// For custom schedulers we also attempt an atomic state transition.
+ /// </param>
+ /// <returns>true if the task was successfully canceled; otherwise, false.</returns>
+ [SecuritySafeCritical]
+ internal bool InternalCancel(bool bCancelNonExecutingOnly)
+ {
+ Contract.Requires((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) == 0, "Task.InternalCancel() did not expect promise-style task");
+
+ bool bPopSucceeded = false;
+ bool mustCleanup = false;
+
+ TaskSchedulerException tse = null;
+
+ // If started, and running in a task context, we can try to pop the chore.
+ if ((m_stateFlags & TASK_STATE_STARTED) != 0)
+ {
+ TaskScheduler ts = m_taskScheduler;
+
+ try
+ {
+ bPopSucceeded = (ts != null) && ts.TryDequeue(this);
+ }
+ catch (Exception e)
+ {
+ // TryDequeue threw. We don't know whether the task was properly dequeued or not. So we must let the rest of
+ // the cancellation logic run its course (record the request, attempt atomic state transition and do cleanup where appropriate)
+ // Here we will only record a TaskSchedulerException, which will later be thrown at function exit.
+
+ if (!(e is ThreadAbortException))
+ {
+ tse = new TaskSchedulerException(e);
+ }
+ }
+
+ bool bRequiresAtomicStartTransition = (ts != null && ts.RequiresAtomicStartTransition) || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0);
+
+ if (!bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition)
+ {
+ // The caller requested cancellation of non-invoked tasks only, and TryDequeue was one way of doing it...
+ // Since that seems to have failed, we should now try an atomic state transition (from non-invoked state to canceled)
+ // An atomic transition here is only safe if we know we're on a custom task scheduler, which also forces a CAS on ExecuteEntry
+
+ // Even though this task can't have any children, we should be ready for handling any continuations that
+ // may be attached to it (although currently
+ // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc)
+ mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED);
+
+
+ // PS: This is slightly different from the regular cancellation codepath
+ // since we record the cancellation request *after* doing the state transition.
+ // However that shouldn't matter too much because the task was never invoked, thus can't have children
+ }
+
+ }
+
+ if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup)
+ {
+ // Record the cancellation request.
+ RecordInternalCancellationRequest();
+
+ // Determine whether we need to clean up
+ // This will be the case
+ // 1) if we were able to pop, and we are able to update task state to TASK_STATE_CANCELED
+ // 2) if the task seems to be yet unstarted, and we can transition to
+ // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or
+ // _RAN_TO_COMPLETION or _FAULTED
+ // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style
+ // tasks, and a promise-style task should not enter into this codepath.
+ if (bPopSucceeded)
+ {
+ // hitting this would mean something wrong with the AtomicStateUpdate above
+ Contract.Assert(!mustCleanup, "Possibly an invalid state transition call was made in InternalCancel()");
+
+ // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where
+ // TS.TryDequeue() returns true but the task is still left on the queue.
+ mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED);
+ }
+ else if (!mustCleanup && (m_stateFlags & TASK_STATE_STARTED) == 0)
+ {
+ mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED,
+ TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION |
+ TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED);
+ }
+
+ // do the cleanup (i.e. set completion event and finish continuations)
+ if (mustCleanup)
+ {
+ CancellationCleanupLogic();
+ }
+ }
+
+ if (tse != null)
+ throw tse;
+ else
+ return (mustCleanup);
+ }
+
+ // Breaks out logic for recording a cancellation request
+ internal void RecordInternalCancellationRequest()
+ {
+ // Record the cancellation request.
+ EnsureContingentPropertiesInitialized().m_internalCancellationRequested = CANCELLATION_REQUESTED;
+ }
+
+ // Breaks out logic for recording a cancellation request
+ // This overload should only be used for promise tasks where no cancellation token
+ // was supplied when the task was created.
+ internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord)
+ {
+ RecordInternalCancellationRequest();
+
+ Contract.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task");
+ Contract.Assert(m_contingentProperties.m_cancellationToken == default(CancellationToken));
+
+ // Store the supplied cancellation token as this task's token.
+ // Waiting on this task will then result in an OperationCanceledException containing this token.
+ if (tokenToRecord != default(CancellationToken))
+ {
+ m_contingentProperties.m_cancellationToken = tokenToRecord;
+ }
+ }
+
+ // Breaks out logic for recording a cancellation request
+ // This overload should only be used for promise tasks where no cancellation token
+ // was supplied when the task was created.
+ internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object cancellationException)
+ {
+ RecordInternalCancellationRequest(tokenToRecord);
+
+ // Store the supplied cancellation exception
+ if (cancellationException != null)
+ {
+#if DEBUG
+ var oce = cancellationException as OperationCanceledException;
+ if (oce == null)
+ {
+ var edi = cancellationException as ExceptionDispatchInfo;
+ Contract.Assert(edi != null, "Expected either an OCE or an EDI");
+ oce = edi.SourceException as OperationCanceledException;
+ Contract.Assert(oce != null, "Expected EDI to contain an OCE");
+ }
+ Contract.Assert(oce.CancellationToken == tokenToRecord,
+ "Expected OCE's token to match the provided token.");
+#endif
+ AddException(cancellationException, representsCancellation: true);
+ }
+ }
+
+ // ASSUMES THAT A SUCCESSFUL CANCELLATION HAS JUST OCCURRED ON THIS TASK!!!
+ // And this method should be called at most once per task.
+ internal void CancellationCleanupLogic()
+ {
+ Contract.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved.");
+ // I'd like to do this, but there is a small window for a race condition. If someone calls Wait() between InternalCancel() and
+ // here, that will set m_completionEvent, leading to a meaningless/harmless assertion.
+ //Contract.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set.");
+
+ // This may have been set already, but we need to make sure.
+ Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
+
+ // Fire completion event if it has been lazily initialized
+ var cp = Volatile.Read(ref m_contingentProperties);
+ if (cp != null)
+ {
+ cp.SetCompleted();
+ cp.DeregisterCancellationCallback();
+ }
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+
+ // Notify parents, fire continuations, other cleanup.
+ FinishStageThree();
+ }
+
+
+ /// <summary>
+ /// Sets the task's cancellation acknowledged flag.
+ /// </summary>
+ private void SetCancellationAcknowledged()
+ {
+ Contract.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task");
+ Contract.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled");
+
+ m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED;
+ }
+
+
+ //
+ // Continuation passing functionality (aka ContinueWith)
+ //
+
+
+
+
+ /// <summary>
+ /// Runs all of the continuations, as appropriate.
+ /// </summary>
+ [SecuritySafeCritical] // for AwaitTaskContinuation.RunOrScheduleAction
+ internal void FinishContinuations()
+ {
+ // Atomically store the fact that this task is completing. From this point on, the adding of continuations will
+ // result in the continuations being run/launched directly rather than being added to the continuation list.
+ object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel);
+ TplEtwProvider etw = TplEtwProvider.Log;
+ bool tplEtwProviderLoggingEnabled = etw.IsEnabled();
+ if (tplEtwProviderLoggingEnabled)
+ {
+ etw.RunningContinuation(Id, continuationObject);
+ }
+
+ // If continuationObject == null, then we don't have any continuations to process
+ if (continuationObject != null)
+ {
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification);
+
+ // Skip synchronous execution of continuations if this task's thread was aborted
+ bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
+ (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
+ ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
+
+ // Handle the single-Action case
+ Action singleAction = continuationObject as Action;
+ if (singleAction != null)
+ {
+ AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask);
+ LogFinishCompletionNotification();
+ return;
+ }
+
+ // Handle the single-ITaskCompletionAction case
+ ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction;
+ if (singleTaskCompletionAction != null)
+ {
+ if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode)
+ {
+ singleTaskCompletionAction.Invoke(this);
+ }
+ else
+ {
+ ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false);
+ }
+ LogFinishCompletionNotification();
+ return;
+ }
+
+ // Handle the single-TaskContinuation case
+ TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation;
+ if (singleTaskContinuation != null)
+ {
+ singleTaskContinuation.Run(this, bCanInlineContinuations);
+ LogFinishCompletionNotification();
+ return;
+ }
+
+ // Not a single; attempt to cast as list
+ List<object> continuations = continuationObject as List<object>;
+
+ if (continuations == null)
+ {
+ LogFinishCompletionNotification();
+ return; // Not a single or a list; just return
+ }
+
+ //
+ // Begin processing of continuation list
+ //
+
+ // Wait for any concurrent adds or removes to be retired
+ lock (continuations) { }
+ int continuationCount = continuations.Count;
+
+ // Fire the asynchronous continuations first ...
+ for (int i = 0; i < continuationCount; i++)
+ {
+ // Synchronous continuation tasks will have the ExecuteSynchronously option,
+ // and we're looking for asynchronous tasks...
+ var tc = continuations[i] as StandardTaskContinuation;
+ if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0)
+ {
+ if (tplEtwProviderLoggingEnabled)
+ {
+ etw.RunningContinuationList(Id, i, tc);
+ }
+ continuations[i] = null; // so that we can skip this later
+ tc.Run(this, bCanInlineContinuations);
+ }
+ }
+
+ // ... and then fire the synchronous continuations (if there are any).
+ // This includes ITaskCompletionAction, AwaitTaskContinuations, and
+ // Action delegates, which are all by default implicitly synchronous.
+ for (int i = 0; i < continuationCount; i++)
+ {
+ object currentContinuation = continuations[i];
+ if (currentContinuation == null) continue;
+ continuations[i] = null; // to enable free'ing up memory earlier
+ if (tplEtwProviderLoggingEnabled)
+ {
+ etw.RunningContinuationList(Id, i, currentContinuation);
+ }
+
+ // If the continuation is an Action delegate, it came from an await continuation,
+ // and we should use AwaitTaskContinuation to run it.
+ Action ad = currentContinuation as Action;
+ if (ad != null)
+ {
+ AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask);
+ }
+ else
+ {
+ // If it's a TaskContinuation object of some kind, invoke it.
+ TaskContinuation tc = currentContinuation as TaskContinuation;
+ if (tc != null)
+ {
+ // We know that this is a synchronous continuation because the
+ // asynchronous ones have been weeded out
+ tc.Run(this, bCanInlineContinuations);
+ }
+ // Otherwise, it must be an ITaskCompletionAction, so invoke it.
+ else
+ {
+ Contract.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction");
+ var action = (ITaskCompletionAction)currentContinuation;
+
+ if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode)
+ {
+ action.Invoke(this);
+ }
+ else
+ {
+ ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false);
+ }
+ }
+ }
+ }
+
+ LogFinishCompletionNotification();
+ }
+ }
+
+ private void LogFinishCompletionNotification()
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.CompletionNotification);
+ }
+
+ #region Continuation methods
+
+ #region Action<Task> continuation
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task> continuationAction)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark parameter.
+ private Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ // Throw on continuation with null action
+ if (continuationAction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
+ }
+
+ // Throw on continuation with null TaskScheduler
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
+
+ Task continuationTask = new ContinuationTaskFromTask(
+ this, continuationAction, null,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+ #endregion
+
+ #region Action<Task, Object> continuation
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task, Object> continuationAction, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark parameter.
+ private Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ // Throw on continuation with null action
+ if (continuationAction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
+ }
+
+ // Throw on continuation with null TaskScheduler
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
+
+ Task continuationTask = new ContinuationTaskFromTask(
+ this, continuationAction, state,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+
+ #endregion
+
+ #region Func<Task, TResult> continuation
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken),
+ TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark parameter.
+ private Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ // Throw on continuation with null function
+ if (continuationFunction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ }
+
+ // Throw on continuation with null task scheduler
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
+
+ Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
+ this, continuationFunction, null,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+ #endregion
+
+ #region Func<Task, Object, TResult> continuation
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken),
+ TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task"/> completes.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark parameter.
+ private Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ // Throw on continuation with null function
+ if (continuationFunction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ }
+
+ // Throw on continuation with null task scheduler
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ Contract.EndContractBlock();
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
+
+ Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
+ this, continuationFunction, state,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+ #endregion
+
+ /// <summary>
+ /// Converts TaskContinuationOptions to TaskCreationOptions, and also does
+ /// some validity checking along the way.
+ /// </summary>
+ /// <param name="continuationOptions">Incoming TaskContinuationOptions</param>
+ /// <param name="creationOptions">Outgoing TaskCreationOptions</param>
+ /// <param name="internalOptions">Outgoing InternalTaskOptions</param>
+ internal static void CreationOptionsFromContinuationOptions(
+ TaskContinuationOptions continuationOptions,
+ out TaskCreationOptions creationOptions,
+ out InternalTaskOptions internalOptions)
+ {
+ // This is used a couple of times below
+ TaskContinuationOptions NotOnAnything =
+ TaskContinuationOptions.NotOnCanceled |
+ TaskContinuationOptions.NotOnFaulted |
+ TaskContinuationOptions.NotOnRanToCompletion;
+
+ TaskContinuationOptions creationOptionsMask =
+ TaskContinuationOptions.PreferFairness |
+ TaskContinuationOptions.LongRunning |
+ TaskContinuationOptions.DenyChildAttach |
+ TaskContinuationOptions.HideScheduler |
+ TaskContinuationOptions.AttachedToParent|
+ TaskContinuationOptions.RunContinuationsAsynchronously;
+
+ // Check that LongRunning and ExecuteSynchronously are not specified together
+ TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning;
+ if ((continuationOptions & illegalMask) == illegalMask)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_ESandLR);
+ }
+
+ // Check that no illegal options were specified
+ if ((continuationOptions &
+ ~(creationOptionsMask | NotOnAnything |
+ TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions);
+ }
+
+ // Check that we didn't specify "not on anything"
+ if ((continuationOptions & NotOnAnything) == NotOnAnything)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_NotOnAnything);
+ }
+
+ // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions
+ creationOptions = (TaskCreationOptions)(continuationOptions & creationOptionsMask);
+
+ // internalOptions has at least ContinuationTask ...
+ internalOptions = InternalTaskOptions.ContinuationTask;
+
+ // ... and possibly LazyCancellation
+ if ((continuationOptions & TaskContinuationOptions.LazyCancellation) != 0)
+ internalOptions |= InternalTaskOptions.LazyCancellation;
+ }
+
+
+ /// <summary>
+ /// Registers the continuation and possibly runs it (if the task is already finished).
+ /// </summary>
+ /// <param name="continuationTask">The continuation task itself.</param>
+ /// <param name="scheduler">TaskScheduler with which to associate continuation task.</param>
+ /// <param name="options">Restrictions on when the continuation becomes active.</param>
+ internal void ContinueWithCore(Task continuationTask,
+ TaskScheduler scheduler,
+ CancellationToken cancellationToken,
+ TaskContinuationOptions options)
+ {
+ Contract.Requires(continuationTask != null, "Task.ContinueWithCore(): null continuationTask");
+ Contract.Requires(scheduler != null, "Task.ContinueWithCore(): null scheduler");
+ Contract.Requires(!continuationTask.IsCompleted, "Did not expect continuationTask to be completed");
+
+ // Create a TaskContinuation
+ TaskContinuation continuation = new StandardTaskContinuation(continuationTask, options, scheduler);
+
+ // If cancellationToken is cancellable, then assign it.
+ if (cancellationToken.CanBeCanceled)
+ {
+ if (IsCompleted || cancellationToken.IsCancellationRequested)
+ {
+ // If the antecedent has completed, then we will not be queuing up
+ // the continuation in the antecedent's continuation list. Likewise,
+ // if the cancellationToken has been canceled, continuationTask will
+ // be completed in the AssignCancellationToken call below, and there
+ // is no need to queue the continuation to the antecedent's continuation
+ // list. In either of these two cases, we will pass "null" for the antecedent,
+ // meaning "the cancellation callback should not attempt to remove the
+ // continuation from its antecedent's continuation list".
+ continuationTask.AssignCancellationToken(cancellationToken, null, null);
+ }
+ else
+ {
+ // The antecedent is not yet complete, so there is a pretty good chance
+ // that the continuation will be queued up in the antecedent. Assign the
+ // cancellation token with information about the antecedent, so that the
+ // continuation can be dequeued upon the signalling of the token.
+ //
+ // It's possible that the antecedent completes before the call to AddTaskContinuation,
+ // and that is a benign race condition. It just means that the cancellation will result in
+ // a futile search of the antecedent's continuation list.
+ continuationTask.AssignCancellationToken(cancellationToken, this, continuation);
+ }
+ }
+
+ // In the case of a pre-canceled token, continuationTask will have been completed
+ // in a Canceled state by now. If such is the case, there is no need to go through
+ // the motions of queuing up the continuation for eventual execution.
+ if (!continuationTask.IsCompleted)
+ {
+ // We need additional correlation produced here to ensure that at least the continuation
+ // code will be correlatable to the currrent activity that initiated "this" task:
+ // . when the antecendent ("this") is a promise we have very little control over where
+ // the code for the promise will run (e.g. it can be a task from a user provided
+ // TaskCompletionSource or from a classic Begin/End async operation); this user or
+ // system code will likely not have stamped an activity id on the thread, so there's
+ // generally no easy correlation that can be provided between the current activity
+ // and the promise. Also the continuation code may run practically on any thread.
+ // Since there may be no correlation between the current activity and the TCS's task
+ // activity, we ensure we at least create a correlation from the current activity to
+ // the continuation that runs when the promise completes.
+ if ((this.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0 &&
+ !(this is ITaskCompletionAction))
+ {
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.IsEnabled())
+ {
+ etwLog.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, Task.CurrentId ?? 0, continuationTask.Id);
+ }
+ }
+
+ // Attempt to enqueue the continuation
+ bool continuationQueued = AddTaskContinuation(continuation, addBeforeOthers: false);
+
+ // If the continuation was not queued (because the task completed), then run it now.
+ if (!continuationQueued) continuation.Run(this, bCanInlineContinuationTask: true);
+ }
+ }
+ #endregion
+
+ // Adds a lightweight completion action to a task. This is similar to a continuation
+ // task except that it is stored as an action, and thus does not require the allocation/
+ // execution resources of a continuation task.
+ //
+ // Used internally by ContinueWhenAll() and ContinueWhenAny().
+ internal void AddCompletionAction(ITaskCompletionAction action)
+ {
+ AddCompletionAction(action, addBeforeOthers: false);
+ }
+
+ private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
+ {
+ if (!AddTaskContinuation(action, addBeforeOthers))
+ action.Invoke(this); // run the action directly if we failed to queue the continuation (i.e., the task completed)
+ }
+
+ // Support method for AddTaskContinuation that takes care of multi-continuation logic.
+ // Returns true if and only if the continuation was successfully queued.
+ // THIS METHOD ASSUMES THAT m_continuationObject IS NOT NULL. That case was taken
+ // care of in the calling method, AddTaskContinuation().
+ private bool AddTaskContinuationComplex(object tc, bool addBeforeOthers)
+ {
+ Contract.Requires(tc != null, "Expected non-null tc object in AddTaskContinuationComplex");
+
+ object oldValue = m_continuationObject;
+
+ // Logic for the case where we were previously storing a single continuation
+ if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>)))
+ {
+ // Construct a new TaskContinuation list
+ List<object> newList = new List<object>();
+
+ // Add in the old single value
+ newList.Add(oldValue);
+
+ // Now CAS in the new list
+ Interlocked.CompareExchange(ref m_continuationObject, newList, oldValue);
+
+ // We might be racing against another thread converting the single into
+ // a list, or we might be racing against task completion, so resample "list"
+ // below.
+ }
+
+ // m_continuationObject is guaranteed at this point to be either a List or
+ // s_taskCompletionSentinel.
+ List<object> list = m_continuationObject as List<object>;
+ Contract.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel),
+ "Expected m_continuationObject to be list or sentinel");
+
+ // If list is null, it can only mean that s_taskCompletionSentinel has been exchanged
+ // into m_continuationObject. Thus, the task has completed and we should return false
+ // from this method, as we will not be queuing up the continuation.
+ if (list != null)
+ {
+ lock (list)
+ {
+ // It is possible for the task to complete right after we snap the copy of
+ // the list. If so, then fall through and return false without queuing the
+ // continuation.
+ if (m_continuationObject != s_taskCompletionSentinel)
+ {
+ // Before growing the list we remove possible null entries that are the
+ // result from RemoveContinuations()
+ if (list.Count == list.Capacity)
+ {
+ list.RemoveAll(s_IsTaskContinuationNullPredicate);
+ }
+
+ if (addBeforeOthers)
+ list.Insert(0, tc);
+ else
+ list.Add(tc);
+
+ return true; // continuation successfully queued, so return true.
+ }
+ }
+ }
+
+ // We didn't succeed in queuing the continuation, so return false.
+ return false;
+ }
+
+ // Record a continuation task or action.
+ // Return true if and only if we successfully queued a continuation.
+ private bool AddTaskContinuation(object tc, bool addBeforeOthers)
+ {
+ Contract.Requires(tc != null);
+
+ // Make sure that, if someone calls ContinueWith() right after waiting for the predecessor to complete,
+ // we don't queue up a continuation.
+ if (IsCompleted) return false;
+
+ // Try to just jam tc into m_continuationObject
+ if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
+ {
+ // If we get here, it means that we failed to CAS tc into m_continuationObject.
+ // Therefore, we must go the more complicated route.
+ return AddTaskContinuationComplex(tc, addBeforeOthers);
+ }
+ else return true;
+ }
+
+ // Removes a continuation task from m_continuations
+ internal void RemoveContinuation(object continuationObject) // could be TaskContinuation or Action<Task>
+ {
+ // We need to snap a local reference to m_continuations since reading a volatile object is more costly.
+ // Also to prevent the value to be changed as result of a race condition with another method.
+ object continuationsLocalRef = m_continuationObject;
+
+ // Task is completed. Nothing to do here.
+ if (continuationsLocalRef == s_taskCompletionSentinel) return;
+
+ List<object> continuationsLocalListRef = continuationsLocalRef as List<object>;
+
+ if (continuationsLocalListRef == null)
+ {
+ // This is not a list. If we have a single object (the one we want to remove) we try to replace it with an empty list.
+ // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic.
+ if (Interlocked.CompareExchange(ref m_continuationObject, new List<object>(), continuationObject) != continuationObject)
+ {
+ // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List
+ // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel.
+ // So we should try to get a list one more time
+ continuationsLocalListRef = m_continuationObject as List<object>;
+ }
+ else
+ {
+ // Exchange was successful so we can skip the last comparison
+ return;
+ }
+ }
+
+ // if continuationsLocalRef == null it means s_taskCompletionSentinel has been set already and there is nothing else to do.
+ if (continuationsLocalListRef != null)
+ {
+ lock (continuationsLocalListRef)
+ {
+ // There is a small chance that this task completed since we took a local snapshot into
+ // continuationsLocalRef. In that case, just return; we don't want to be manipulating the
+ // continuation list as it is being processed.
+ if (m_continuationObject == s_taskCompletionSentinel) return;
+
+ // Find continuationObject in the continuation list
+ int index = continuationsLocalListRef.IndexOf(continuationObject);
+
+ if (index != -1)
+ {
+ // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up"
+ continuationsLocalListRef[index] = null;
+
+ }
+ }
+ }
+ }
+
+ // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex()
+ private readonly static Predicate<object> s_IsTaskContinuationNullPredicate =
+ new Predicate<object>((tc) => { return (tc == null); });
+
+
+ //
+ // Wait methods
+ //
+
+ /// <summary>
+ /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
+ /// the execution of at least one of the <see cref="Task"/> instances.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static void WaitAll(params Task[] tasks)
+ {
+#if DEBUG
+ bool waitResult =
+#endif
+ WaitAll(tasks, Timeout.Infinite);
+
+#if DEBUG
+ Contract.Assert(waitResult, "expected wait to succeed");
+#endif
+ }
+
+ /// <summary>
+ /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <returns>
+ /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
+ /// otherwise, false.
+ /// </returns>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="timeout">
+ /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
+ /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
+ /// the execution of at least one of the <see cref="Task"/> instances.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
+ /// infinite time-out -or- timeout is greater than
+ /// <see cref="System.Int32.MaxValue"/>.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static bool WaitAll(Task[] tasks, TimeSpan timeout)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
+ }
+
+ return WaitAll(tasks, (int)totalMilliseconds);
+
+ }
+
+ /// <summary>
+ /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <returns>
+ /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
+ /// otherwise, false.
+ /// </returns>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.</param>
+ /// <param name="tasks">An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
+ /// the execution of at least one of the <see cref="Task"/> instances.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static bool WaitAll(Task[] tasks, int millisecondsTimeout)
+ {
+ return WaitAll(tasks, millisecondsTimeout, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <returns>
+ /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
+ /// otherwise, false.
+ /// </returns>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
+ /// the execution of at least one of the <see cref="Task"/> instances.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static void WaitAll(Task[] tasks, CancellationToken cancellationToken)
+ {
+ WaitAll(tasks, Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <returns>
+ /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
+ /// otherwise, false.
+ /// </returns>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.AggregateException">
+ /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
+ /// the execution of at least one of the <see cref="Task"/> instances.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ if (tasks == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ }
+ if (millisecondsTimeout < -1)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
+ }
+ Contract.EndContractBlock();
+
+ cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
+
+ //
+ // In this WaitAll() implementation we have 2 alternate code paths for a task to be handled:
+ // CODEPATH1: skip an already completed task, CODEPATH2: actually wait on tasks
+ // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths
+ //
+
+ List<Exception> exceptions = null;
+ List<Task> waitedOnTaskList = null;
+ List<Task> notificationTasks = null;
+
+ // If any of the waited-upon tasks end as Faulted or Canceled, set these to true.
+ bool exceptionSeen = false, cancellationSeen = false;
+
+ bool returnValue = true;
+
+ // Collects incomplete tasks in "waitedOnTaskList"
+ for (int i = tasks.Length - 1; i >= 0; i--)
+ {
+ Task task = tasks[i];
+
+ if (task == null)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks);
+ }
+
+ bool taskIsCompleted = task.IsCompleted;
+ if (!taskIsCompleted)
+ {
+ // try inlining the task only if we have an infinite timeout and an empty cancellation token
+ if (millisecondsTimeout != Timeout.Infinite || cancellationToken.CanBeCanceled)
+ {
+ // We either didn't attempt inline execution because we had a non-infinite timeout or we had a cancellable token.
+ // In all cases we need to do a full wait on the task (=> add its event into the list.)
+ AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
+ }
+ else
+ {
+ // We are eligible for inlining. If it doesn't work, we'll do a full wait.
+ taskIsCompleted = task.WrappedTryRunInline() && task.IsCompleted; // A successful TryRunInline doesn't guarantee completion
+ if (!taskIsCompleted) AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
+ }
+ }
+
+ if (taskIsCompleted)
+ {
+ if (task.IsFaulted) exceptionSeen = true;
+ else if (task.IsCanceled) cancellationSeen = true;
+ if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
+ }
+ }
+
+ if (waitedOnTaskList != null)
+ {
+ // Block waiting for the tasks to complete.
+ returnValue = WaitAllBlockingCore(waitedOnTaskList, millisecondsTimeout, cancellationToken);
+
+ // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is
+ // attached and one of these tasks requires it, that we notify the debugger of a wait completion.
+ if (returnValue)
+ {
+ // Add any exceptions for this task to the collection, and if it's wait
+ // notification bit is set, store it to operate on at the end.
+ foreach (var task in waitedOnTaskList)
+ {
+ if (task.IsFaulted) exceptionSeen = true;
+ else if (task.IsCanceled) cancellationSeen = true;
+ if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
+ }
+ }
+
+ // We need to prevent the tasks array from being GC'ed until we come out of the wait.
+ // This is necessary so that the Parallel Debugger can traverse it during the long wait and
+ // deduce waiter/waitee relationships
+ GC.KeepAlive(tasks);
+ }
+
+ // Now that we're done and about to exit, if the wait completed and if we have
+ // any tasks with a notification bit set, signal the debugger if any requires it.
+ if (returnValue && notificationTasks != null)
+ {
+ // Loop through each task tha that had its bit set, and notify the debugger
+ // about the first one that requires it. The debugger will reset the bit
+ // for any tasks we don't notify of as soon as we break, so we only need to notify
+ // for one.
+ foreach (var task in notificationTasks)
+ {
+ if (task.NotifyDebuggerOfWaitCompletionIfNecessary()) break;
+ }
+ }
+
+ // If one or more threw exceptions, aggregate and throw them.
+ if (returnValue && (exceptionSeen || cancellationSeen))
+ {
+ // If the WaitAll was canceled and tasks were canceled but not faulted,
+ // prioritize throwing an OCE for canceling the WaitAll over throwing an
+ // AggregateException for all of the canceled Tasks. This helps
+ // to bring determinism to an otherwise non-determistic case of using
+ // the same token to cancel both the WaitAll and the Tasks.
+ if (!exceptionSeen) cancellationToken.ThrowIfCancellationRequested();
+
+ // Now gather up and throw all of the exceptions.
+ foreach (var task in tasks) AddExceptionsForCompletedTask(ref exceptions, task);
+ Contract.Assert(exceptions != null, "Should have seen at least one exception");
+ ThrowHelper.ThrowAggregateException(exceptions);
+ }
+
+ return returnValue;
+ }
+
+ /// <summary>Adds an element to the list, initializing the list if it's null.</summary>
+ /// <typeparam name="T">Specifies the type of data stored in the list.</typeparam>
+ /// <param name="item">The item to add.</param>
+ /// <param name="list">The list.</param>
+ /// <param name="initSize">The size to which to initialize the list if the list is null.</param>
+ private static void AddToList<T>(T item, ref List<T> list, int initSize)
+ {
+ if (list == null) list = new List<T>(initSize);
+ list.Add(item);
+ }
+
+ /// <summary>Performs a blocking WaitAll on the vetted list of tasks.</summary>
+ /// <param name="tasks">The tasks, which have already been checked and filtered for completion.</param>
+ /// <param name="millisecondsTimeout">The timeout.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>true if all of the tasks completed; otherwise, false.</returns>
+ private static bool WaitAllBlockingCore(List<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ Contract.Assert(tasks != null, "Expected a non-null list of tasks");
+ Contract.Assert(tasks.Count > 0, "Expected at least one task");
+
+ bool waitCompleted = false;
+ var mres = new SetOnCountdownMres(tasks.Count);
+ try
+ {
+ foreach (var task in tasks)
+ {
+ task.AddCompletionAction(mres, addBeforeOthers: true);
+ }
+ waitCompleted = mres.Wait(millisecondsTimeout, cancellationToken);
+ }
+ finally
+ {
+ if (!waitCompleted)
+ {
+ foreach (var task in tasks)
+ {
+ if (!task.IsCompleted) task.RemoveContinuation(mres);
+ }
+ }
+ // It's ok that we don't dispose of the MRES here, as we never
+ // access the MRES' WaitHandle, and thus no finalizable resources
+ // are actually created. We don't always just Dispose it because
+ // a continuation that's accessing the MRES could still be executing.
+ }
+ return waitCompleted;
+ }
+
+ // A ManualResetEventSlim that will get Set after Invoke is called count times.
+ // This allows us to replace this logic:
+ // var mres = new ManualResetEventSlim(tasks.Count);
+ // Action<Task> completionAction = delegate { if(Interlocked.Decrement(ref count) == 0) mres.Set(); };
+ // foreach(var task in tasks) task.AddCompletionAction(completionAction);
+ // with this logic:
+ // var mres = new SetOnCountdownMres(tasks.Count);
+ // foreach(var task in tasks) task.AddCompletionAction(mres);
+ // which saves a couple of allocations.
+ //
+ // Used in WaitAllBlockingCore (above).
+ private sealed class SetOnCountdownMres : ManualResetEventSlim, ITaskCompletionAction
+ {
+ private int _count;
+
+ internal SetOnCountdownMres(int count)
+ {
+ Contract.Assert(count > 0, "Expected count > 0");
+ _count = count;
+ }
+
+ public void Invoke(Task completingTask)
+ {
+ if (Interlocked.Decrement(ref _count) == 0) Set();
+ Contract.Assert(_count >= 0, "Count should never go below 0");
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return false; } }
+ }
+
+ /// <summary>
+ /// Internal WaitAll implementation which is meant to be used with small number of tasks,
+ /// optimized for Parallel.Invoke and other structured primitives.
+ /// </summary>
+ internal static void FastWaitAll(Task[] tasks)
+ {
+ Contract.Requires(tasks != null);
+
+ List<Exception> exceptions = null;
+
+ // Collects incomplete tasks in "waitedOnTaskList" and their cooperative events in "cooperativeEventList"
+ for (int i = tasks.Length - 1; i >= 0; i--)
+ {
+ if (!tasks[i].IsCompleted)
+ {
+ // Just attempting to inline here... result doesn't matter.
+ // We'll do a second pass to do actual wait on each task, and to aggregate their exceptions.
+ // If the task is inlined here, it will register as IsCompleted in the second pass
+ // and will just give us the exception.
+ tasks[i].WrappedTryRunInline();
+ }
+ }
+
+ // Wait on the tasks.
+ for (int i = tasks.Length - 1; i >= 0; i--)
+ {
+ var task = tasks[i];
+ task.SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
+ AddExceptionsForCompletedTask(ref exceptions, task);
+
+ // Note that unlike other wait code paths, we do not check
+ // task.NotifyDebuggerOfWaitCompletionIfNecessary() here, because this method is currently
+ // only used from contexts where the tasks couldn't have that bit set, namely
+ // Parallel.Invoke. If that ever changes, such checks should be added here.
+ }
+
+ // If one or more threw exceptions, aggregate them.
+ if (exceptions != null)
+ {
+ ThrowHelper.ThrowAggregateException(exceptions);
+ }
+ }
+
+ /// <summary>
+ /// This internal function is only meant to be called by WaitAll()
+ /// If the completed task is canceled or it has other exceptions, here we will add those
+ /// into the passed in exception list (which will be lazily initialized here).
+ /// </summary>
+ internal static void AddExceptionsForCompletedTask(ref List<Exception> exceptions, Task t)
+ {
+ AggregateException ex = t.GetExceptions(true);
+ if (ex != null)
+ {
+ // make sure the task's exception observed status is set appropriately
+ // it's possible that WaitAll was called by the parent of an attached child,
+ // this will make sure it won't throw again in the implicit wait
+ t.UpdateExceptionObservedStatus();
+
+ if (exceptions == null)
+ {
+ exceptions = new List<Exception>(ex.InnerExceptions.Count);
+ }
+
+ exceptions.AddRange(ex.InnerExceptions);
+ }
+ }
+
+
+ /// <summary>
+ /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <returns>The index of the completed task in the <paramref name="tasks"/> array argument.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static int WaitAny(params Task[] tasks)
+ {
+ int waitResult = WaitAny(tasks, Timeout.Infinite);
+ Contract.Assert(tasks.Length == 0 || waitResult != -1, "expected wait to succeed");
+ return waitResult;
+ }
+
+ /// <summary>
+ /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="timeout">
+ /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
+ /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+ /// </param>
+ /// <returns>
+ /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
+ /// timeout occurred.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
+ /// infinite time-out -or- timeout is greater than
+ /// <see cref="System.Int32.MaxValue"/>.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static int WaitAny(Task[] tasks, TimeSpan timeout)
+ {
+ long totalMilliseconds = (long)timeout.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
+ }
+
+ return WaitAny(tasks, (int)totalMilliseconds);
+ }
+
+ /// <summary>
+ /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
+ /// </param>
+ /// <returns>
+ /// The index of the completed task in the <paramref name="tasks"/> array argument.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static int WaitAny(Task[] tasks, CancellationToken cancellationToken)
+ {
+ return WaitAny(tasks, Timeout.Infinite, cancellationToken);
+ }
+
+ /// <summary>
+ /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.
+ /// </param>
+ /// <returns>
+ /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
+ /// timeout occurred.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static int WaitAny(Task[] tasks, int millisecondsTimeout)
+ {
+ return WaitAny(tasks, millisecondsTimeout, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
+ /// </summary>
+ /// <param name="tasks">
+ /// An array of <see cref="Task"/> instances on which to wait.
+ /// </param>
+ /// <param name="millisecondsTimeout">
+ /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
+ /// wait indefinitely.
+ /// </param>
+ /// <param name="cancellationToken">
+ /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
+ /// </param>
+ /// <returns>
+ /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
+ /// timeout occurred.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> argument contains a null element.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
+ /// infinite time-out.
+ /// </exception>
+ /// <exception cref="T:System.OperationCanceledException">
+ /// The <paramref name="cancellationToken"/> was canceled.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
+ public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ if (tasks == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ }
+ if (millisecondsTimeout < -1)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
+ }
+ Contract.EndContractBlock();
+
+ cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
+
+ int signaledTaskIndex = -1;
+
+ // Make a pass through the loop to check for any tasks that may have
+ // already been completed, and to verify that no tasks are null.
+
+ for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++)
+ {
+ Task task = tasks[taskIndex];
+
+ if (task == null)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks);
+ }
+
+ if (signaledTaskIndex == -1 && task.IsCompleted)
+ {
+ // We found our first completed task. Store it, but we can't just return here,
+ // as we still need to validate the whole array for nulls.
+ signaledTaskIndex = taskIndex;
+ }
+ }
+
+ if (signaledTaskIndex == -1 && tasks.Length != 0)
+ {
+ Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);
+ bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);
+ if (waitCompleted)
+ {
+ Contract.Assert(firstCompleted.Status == TaskStatus.RanToCompletion);
+ signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result);
+ Contract.Assert(signaledTaskIndex >= 0);
+ }
+ }
+
+ // We need to prevent the tasks array from being GC'ed until we come out of the wait.
+ // This is necessary so that the Parallel Debugger can traverse it during the long wait
+ // and deduce waiter/waitee relationships
+ GC.KeepAlive(tasks);
+
+ // Return the index
+ return signaledTaskIndex;
+ }
+
+ #region FromResult / FromException / FromCanceled
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed successfully with the specified result.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="result">The result to store into the completed task.</param>
+ /// <returns>The successfully completed task.</returns>
+ public static Task<TResult> FromResult<TResult>(TResult result)
+ {
+ return new Task<TResult>(result);
+ }
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="exception">The exception with which to complete the task.</param>
+ /// <returns>The faulted task.</returns>
+ public static Task FromException(Exception exception)
+ {
+ return FromException<VoidTaskResult>(exception);
+ }
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="exception">The exception with which to complete the task.</param>
+ /// <returns>The faulted task.</returns>
+ public static Task<TResult> FromException<TResult>(Exception exception)
+ {
+ if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
+ Contract.EndContractBlock();
+
+ var task = new Task<TResult>();
+ bool succeeded = task.TrySetException(exception);
+ Contract.Assert(succeeded, "This should always succeed on a new task.");
+ return task;
+ }
+
+ /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
+ /// <param name="cancellationToken">The token with which to complete the task.</param>
+ /// <returns>The canceled task.</returns>
+ public static Task FromCanceled(CancellationToken cancellationToken)
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.cancellationToken);
+ Contract.EndContractBlock();
+ return new Task(true, TaskCreationOptions.None, cancellationToken);
+ }
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="cancellationToken">The token with which to complete the task.</param>
+ /// <returns>The canceled task.</returns>
+ public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.cancellationToken);
+ Contract.EndContractBlock();
+ return new Task<TResult>(true, default(TResult), TaskCreationOptions.None, cancellationToken);
+ }
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified exception.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="exception">The exception with which to complete the task.</param>
+ /// <returns>The canceled task.</returns>
+ internal static Task<TResult> FromCancellation<TResult>(OperationCanceledException exception)
+ {
+ if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
+ Contract.EndContractBlock();
+
+ var task = new Task<TResult>();
+ bool succeeded = task.TrySetCanceled(exception.CancellationToken, exception);
+ Contract.Assert(succeeded, "This should always succeed on a new task.");
+ return task;
+ }
+
+ /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
+ /// <param name="cancellationToken">The token with which to complete the task.</param>
+ /// <returns>The canceled task.</returns>
+ [FriendAccessAllowed]
+ internal static Task FromCancellation(CancellationToken cancellationToken)
+ {
+ return FromCanceled(cancellationToken);
+ }
+
+ /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
+ /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
+ /// <param name="cancellationToken">The token with which to complete the task.</param>
+ /// <returns>The canceled task.</returns>
+ [FriendAccessAllowed]
+ internal static Task<TResult> FromCancellation<TResult>(CancellationToken cancellationToken)
+ {
+ return FromCanceled<TResult>(cancellationToken);
+ }
+
+ #endregion
+
+ #region Run methods
+
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
+ /// </summary>
+ /// <param name="action">The work to execute asynchronously</param>
+ /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> parameter was null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public static Task Run(Action action)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
+ TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
+ /// </summary>
+ /// <param name="action">The work to execute asynchronously</param>
+ /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
+ /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="action"/> parameter was null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public static Task Run(Action action, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task.InternalStartNew(null, action, null, cancellationToken, TaskScheduler.Default,
+ TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
+ /// </summary>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public static Task<TResult> Run<TResult>(Func<TResult> function)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(null, function, default(CancellationToken),
+ TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
+ /// </summary>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
+ /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(null, function, cancellationToken,
+ TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a proxy for the
+ /// Task returned by <paramref name="function"/>.
+ /// </summary>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ public static Task Run(Func<Task> function)
+ {
+ return Run(function, default(CancellationToken));
+ }
+
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a proxy for the
+ /// Task returned by <paramref name="function"/>.
+ /// </summary>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
+ /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
+ /// </exception>
+ public static Task Run(Func<Task> function, CancellationToken cancellationToken)
+ {
+ // Check arguments
+ if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
+ Contract.EndContractBlock();
+
+ if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ {
+ cancellationToken.ThrowIfSourceDisposed();
+ }
+
+ // Short-circuit if we are given a pre-canceled token
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ // Kick off initial Task, which will call the user-supplied function and yield a Task.
+ Task<Task> task1 = Task<Task>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+ // Create a promise-style Task to be used as a proxy for the operation
+ // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
+ UnwrapPromise<VoidTaskResult> promise = new UnwrapPromise<VoidTaskResult>(task1, lookForOce: true);
+
+ return promise;
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a proxy for the
+ /// Task(TResult) returned by <paramref name="function"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)
+ {
+ return Run(function, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Queues the specified work to run on the ThreadPool and returns a proxy for the
+ /// Task(TResult) returned by <paramref name="function"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
+ /// <param name="function">The work to execute asynchronously</param>
+ /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
+ /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="function"/> parameter was null.
+ /// </exception>
+ public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken)
+ {
+ // Check arguments
+ if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
+ Contract.EndContractBlock();
+
+ if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
+ {
+ cancellationToken.ThrowIfSourceDisposed();
+ }
+
+ // Short-circuit if we are given a pre-canceled token
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled<TResult>(cancellationToken);
+
+ // Kick off initial Task, which will call the user-supplied function and yield a Task.
+ Task<Task<TResult>> task1 = Task<Task<TResult>>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+ // Create a promise-style Task to be used as a proxy for the operation
+ // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
+ UnwrapPromise<TResult> promise = new UnwrapPromise<TResult>(task1, lookForOce: true);
+
+ return promise;
+ }
+
+
+ #endregion
+
+ #region Delay methods
+
+ /// <summary>
+ /// Creates a Task that will complete after a time delay.
+ /// </summary>
+ /// <param name="delay">The time span to wait before completing the returned Task</param>
+ /// <returns>A Task that represents the time delay</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
+ /// </exception>
+ /// <remarks>
+ /// After the specified time delay, the Task is completed in RanToCompletion state.
+ /// </remarks>
+ public static Task Delay(TimeSpan delay)
+ {
+ return Delay(delay, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Creates a Task that will complete after a time delay.
+ /// </summary>
+ /// <param name="delay">The time span to wait before completing the returned Task</param>
+ /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
+ /// <returns>A Task that represents the time delay</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The provided <paramref name="cancellationToken"/> has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
+ /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
+ /// delay has expired.
+ /// </remarks>
+ public static Task Delay(TimeSpan delay, CancellationToken cancellationToken)
+ {
+ long totalMilliseconds = (long)delay.TotalMilliseconds;
+ if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.delay, ExceptionResource.Task_Delay_InvalidDelay);
+ }
+
+ return Delay((int)totalMilliseconds, cancellationToken);
+ }
+
+ /// <summary>
+ /// Creates a Task that will complete after a time delay.
+ /// </summary>
+ /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
+ /// <returns>A Task that represents the time delay</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="millisecondsDelay"/> is less than -1.
+ /// </exception>
+ /// <remarks>
+ /// After the specified time delay, the Task is completed in RanToCompletion state.
+ /// </remarks>
+ public static Task Delay(int millisecondsDelay)
+ {
+ return Delay(millisecondsDelay, default(CancellationToken));
+ }
+
+ /// <summary>
+ /// Creates a Task that will complete after a time delay.
+ /// </summary>
+ /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
+ /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
+ /// <returns>A Task that represents the time delay</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="millisecondsDelay"/> is less than -1.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The provided <paramref name="cancellationToken"/> has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
+ /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
+ /// delay has expired.
+ /// </remarks>
+ public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
+ {
+ // Throw on non-sensical time
+ if (millisecondsDelay < -1)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsDelay, ExceptionResource.Task_Delay_InvalidMillisecondsDelay);
+ }
+ Contract.EndContractBlock();
+
+ // some short-cuts in case quick completion is in order
+ if (cancellationToken.IsCancellationRequested)
+ {
+ // return a Task created as already-Canceled
+ return Task.FromCanceled(cancellationToken);
+ }
+ else if (millisecondsDelay == 0)
+ {
+ // return a Task created as already-RanToCompletion
+ return Task.CompletedTask;
+ }
+
+ // Construct a promise-style Task to encapsulate our return value
+ var promise = new DelayPromise(cancellationToken);
+
+ // Register our cancellation token, if necessary.
+ if (cancellationToken.CanBeCanceled)
+ {
+ promise.Registration = cancellationToken.InternalRegisterWithoutEC(state => ((DelayPromise)state).Complete(), promise);
+ }
+
+ // ... and create our timer and make sure that it stays rooted.
+ if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
+ {
+ promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);
+ promise.Timer.KeepRootedWhileScheduled();
+ }
+
+ // Return the timer proxy task
+ return promise;
+ }
+
+ /// <summary>Task that also stores the completion closure and logic for Task.Delay implementation.</summary>
+ private sealed class DelayPromise : Task<VoidTaskResult>
+ {
+ internal DelayPromise(CancellationToken token)
+ : base()
+ {
+ this.Token = token;
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Delay", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+ }
+
+ internal readonly CancellationToken Token;
+ internal CancellationTokenRegistration Registration;
+ internal Timer Timer;
+
+ internal void Complete()
+ {
+ // Transition the task to completed.
+ bool setSucceeded;
+
+ if (Token.IsCancellationRequested)
+ {
+ setSucceeded = TrySetCanceled(Token);
+ }
+ else
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ setSucceeded = TrySetResult(default(VoidTaskResult));
+ }
+
+ // If we set the value, also clean up.
+ if (setSucceeded)
+ {
+ if (Timer != null) Timer.Dispose();
+ Registration.Dispose();
+ }
+ }
+ }
+ #endregion
+
+ #region WhenAll
+ /// <summary>
+ /// Creates a task that will complete when all of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
+ /// <remarks>
+ /// <para>
+ /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
+ /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
+ /// </para>
+ /// <para>
+ /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
+ /// </para>
+ /// <para>
+ /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
+ /// </para>
+ /// <para>
+ /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
+ /// state before it's returned to the caller.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> collection contained a null task.
+ /// </exception>
+ public static Task WhenAll(IEnumerable<Task> tasks)
+ {
+ // Take a more efficient path if tasks is actually an array
+ Task[] taskArray = tasks as Task[];
+ if (taskArray != null)
+ {
+ return WhenAll(taskArray);
+ }
+
+ // Skip a List allocation/copy if tasks is a collection
+ ICollection<Task> taskCollection = tasks as ICollection<Task>;
+ if (taskCollection != null)
+ {
+ int index = 0;
+ taskArray = new Task[taskCollection.Count];
+ foreach (var task in tasks)
+ {
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ taskArray[index++] = task;
+ }
+ return InternalWhenAll(taskArray);
+ }
+
+ // Do some argument checking and convert tasks to a List (and later an array).
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ List<Task> taskList = new List<Task>();
+ foreach (Task task in tasks)
+ {
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ taskList.Add(task);
+ }
+
+ // Delegate the rest to InternalWhenAll()
+ return InternalWhenAll(taskList.ToArray());
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when all of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
+ /// <remarks>
+ /// <para>
+ /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
+ /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
+ /// </para>
+ /// <para>
+ /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
+ /// </para>
+ /// <para>
+ /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
+ /// </para>
+ /// <para>
+ /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
+ /// state before it's returned to the caller.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> array contained a null task.
+ /// </exception>
+ public static Task WhenAll(params Task[] tasks)
+ {
+ // Do some argument checking and make a defensive copy of the tasks array
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ Contract.EndContractBlock();
+
+ int taskCount = tasks.Length;
+ if (taskCount == 0) return InternalWhenAll(tasks); // Small optimization in the case of an empty array.
+
+ Task[] tasksCopy = new Task[taskCount];
+ for (int i = 0; i < taskCount; i++)
+ {
+ Task task = tasks[i];
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ tasksCopy[i] = task;
+ }
+
+ // The rest can be delegated to InternalWhenAll()
+ return InternalWhenAll(tasksCopy);
+ }
+
+ // Some common logic to support WhenAll() methods
+ // tasks should be a defensive copy.
+ private static Task InternalWhenAll(Task[] tasks)
+ {
+ Contract.Requires(tasks != null, "Expected a non-null tasks array");
+ return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
+ Task.CompletedTask :
+ new WhenAllPromise(tasks);
+ }
+
+ // A Task<VoidTaskResult> that gets completed when all of its constituent tasks complete.
+ // Completion logic will analyze the antecedents in order to choose completion status.
+ // This type allows us to replace this logic:
+ // Task<VoidTaskResult> promise = new Task<VoidTaskResult>(...);
+ // Action<Task> completionAction = delegate { <completion logic>};
+ // TaskFactory.CommonCWAllLogic(tasksCopy).AddCompletionAction(completionAction);
+ // return promise;
+ // which involves several allocations, with this logic:
+ // return new WhenAllPromise(tasksCopy);
+ // which saves a couple of allocations and enables debugger notification specialization.
+ //
+ // Used in InternalWhenAll(Task[])
+ private sealed class WhenAllPromise : Task<VoidTaskResult>, ITaskCompletionAction
+ {
+ /// <summary>
+ /// Stores all of the constituent tasks. Tasks clear themselves out of this
+ /// array as they complete, but only if they don't have their wait notification bit set.
+ /// </summary>
+ private readonly Task[] m_tasks;
+ /// <summary>The number of tasks remaining to complete.</summary>
+ private int m_count;
+
+ internal WhenAllPromise(Task[] tasks) :
+ base()
+ {
+ Contract.Requires(tasks != null, "Expected a non-null task array");
+ Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
+
+ if (s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+
+ m_tasks = tasks;
+ m_count = tasks.Length;
+
+ foreach (var task in tasks)
+ {
+ if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
+ else task.AddCompletionAction(this); // simple completion action
+ }
+ }
+
+ public void Invoke(Task completedTask)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
+
+ // Decrement the count, and only continue to complete the promise if we're the last one.
+ if (Interlocked.Decrement(ref m_count) == 0)
+ {
+ // Set up some accounting variables
+ List<ExceptionDispatchInfo> observedExceptions = null;
+ Task canceledTask = null;
+
+ // Loop through antecedents:
+ // If any one of them faults, the result will be faulted
+ // If none fault, but at least one is canceled, the result will be canceled
+ // If none fault or are canceled, then result will be RanToCompletion
+ for (int i = 0; i < m_tasks.Length; i++)
+ {
+ var task = m_tasks[i];
+ Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
+
+ if (task.IsFaulted)
+ {
+ if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
+ observedExceptions.AddRange(task.GetExceptionDispatchInfos());
+ }
+ else if (task.IsCanceled)
+ {
+ if (canceledTask == null) canceledTask = task; // use the first task that's canceled
+ }
+
+ // Regardless of completion state, if the task has its debug bit set, transfer it to the
+ // WhenAll task. We must do this before we complete the task.
+ if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
+ else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
+ }
+
+ if (observedExceptions != null)
+ {
+ Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
+
+ //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
+
+ TrySetException(observedExceptions);
+ }
+ else if (canceledTask != null)
+ {
+ TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
+ }
+ else
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ TrySetResult(default(VoidTaskResult));
+ }
+ }
+ Contract.Assert(m_count >= 0, "Count should never go below 0");
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+
+ /// <summary>
+ /// Returns whether we should notify the debugger of a wait completion. This returns
+ /// true iff at least one constituent task has its bit set.
+ /// </summary>
+ internal override bool ShouldNotifyDebuggerOfWaitCompletion
+ {
+ get
+ {
+ return
+ base.ShouldNotifyDebuggerOfWaitCompletion &&
+ Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when all of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
+ /// <remarks>
+ /// <para>
+ /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
+ /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
+ /// </para>
+ /// <para>
+ /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
+ /// </para>
+ /// <para>
+ /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
+ /// The Result of the returned task will be set to an array containing all of the results of the
+ /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
+ /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
+ /// </para>
+ /// <para>
+ /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
+ /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> collection contained a null task.
+ /// </exception>
+ public static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
+ {
+ // Take a more efficient route if tasks is actually an array
+ Task<TResult>[] taskArray = tasks as Task<TResult>[];
+ if (taskArray != null)
+ {
+ return WhenAll<TResult>(taskArray);
+ }
+
+ // Skip a List allocation/copy if tasks is a collection
+ ICollection<Task<TResult>> taskCollection = tasks as ICollection<Task<TResult>>;
+ if (taskCollection != null)
+ {
+ int index = 0;
+ taskArray = new Task<TResult>[taskCollection.Count];
+ foreach (var task in tasks)
+ {
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ taskArray[index++] = task;
+ }
+ return InternalWhenAll<TResult>(taskArray);
+ }
+
+ // Do some argument checking and convert tasks into a List (later an array)
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ List<Task<TResult>> taskList = new List<Task<TResult>>();
+ foreach (Task<TResult> task in tasks)
+ {
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ taskList.Add(task);
+ }
+
+ // Delegate the rest to InternalWhenAll<TResult>().
+ return InternalWhenAll<TResult>(taskList.ToArray());
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when all of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
+ /// <remarks>
+ /// <para>
+ /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
+ /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
+ /// </para>
+ /// <para>
+ /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
+ /// </para>
+ /// <para>
+ /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
+ /// The Result of the returned task will be set to an array containing all of the results of the
+ /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
+ /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
+ /// </para>
+ /// <para>
+ /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
+ /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> array contained a null task.
+ /// </exception>
+ public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)
+ {
+ // Do some argument checking and make a defensive copy of the tasks array
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ Contract.EndContractBlock();
+
+ int taskCount = tasks.Length;
+ if (taskCount == 0) return InternalWhenAll<TResult>(tasks); // small optimization in the case of an empty task array
+
+ Task<TResult>[] tasksCopy = new Task<TResult>[taskCount];
+ for (int i = 0; i < taskCount; i++)
+ {
+ Task<TResult> task = tasks[i];
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ tasksCopy[i] = task;
+ }
+
+ // Delegate the rest to InternalWhenAll<TResult>()
+ return InternalWhenAll<TResult>(tasksCopy);
+ }
+
+ // Some common logic to support WhenAll<TResult> methods
+ private static Task<TResult[]> InternalWhenAll<TResult>(Task<TResult>[] tasks)
+ {
+ Contract.Requires(tasks != null, "Expected a non-null tasks array");
+ return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
+ new Task<TResult[]>(false, new TResult[0], TaskCreationOptions.None, default(CancellationToken)) :
+ new WhenAllPromise<TResult>(tasks);
+ }
+
+ // A Task<T> that gets completed when all of its constituent tasks complete.
+ // Completion logic will analyze the antecedents in order to choose completion status.
+ // See comments for non-generic version of WhenAllPromise class.
+ //
+ // Used in InternalWhenAll<TResult>(Task<TResult>[])
+ private sealed class WhenAllPromise<T> : Task<T[]>, ITaskCompletionAction
+ {
+ /// <summary>
+ /// Stores all of the constituent tasks. Tasks clear themselves out of this
+ /// array as they complete, but only if they don't have their wait notification bit set.
+ /// </summary>
+ private readonly Task<T>[] m_tasks;
+ /// <summary>The number of tasks remaining to complete.</summary>
+ private int m_count;
+
+ internal WhenAllPromise(Task<T>[] tasks) :
+ base()
+ {
+ Contract.Requires(tasks != null, "Expected a non-null task array");
+ Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
+
+ m_tasks = tasks;
+ m_count = tasks.Length;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
+
+ if (s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+
+ foreach (var task in tasks)
+ {
+ if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
+ else task.AddCompletionAction(this); // simple completion action
+ }
+ }
+
+ public void Invoke(Task ignored)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
+
+ // Decrement the count, and only continue to complete the promise if we're the last one.
+ if (Interlocked.Decrement(ref m_count) == 0)
+ {
+ // Set up some accounting variables
+ T[] results = new T[m_tasks.Length];
+ List<ExceptionDispatchInfo> observedExceptions = null;
+ Task canceledTask = null;
+
+ // Loop through antecedents:
+ // If any one of them faults, the result will be faulted
+ // If none fault, but at least one is canceled, the result will be canceled
+ // If none fault or are canceled, then result will be RanToCompletion
+ for (int i = 0; i < m_tasks.Length; i++)
+ {
+ Task<T> task = m_tasks[i];
+ Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
+
+ if (task.IsFaulted)
+ {
+ if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
+ observedExceptions.AddRange(task.GetExceptionDispatchInfos());
+ }
+ else if (task.IsCanceled)
+ {
+ if (canceledTask == null) canceledTask = task; // use the first task that's canceled
+ }
+ else
+ {
+ Contract.Assert(task.Status == TaskStatus.RanToCompletion);
+ results[i] = task.GetResultCore(waitCompletionNotification: false); // avoid Result, which would triggering debug notification
+ }
+
+ // Regardless of completion state, if the task has its debug bit set, transfer it to the
+ // WhenAll task. We must do this before we complete the task.
+ if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
+ else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
+ }
+
+ if (observedExceptions != null)
+ {
+ Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
+
+ //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
+
+ TrySetException(observedExceptions);
+ }
+ else if (canceledTask != null)
+ {
+ TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
+ }
+ else
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+ TrySetResult(results);
+ }
+ }
+ Contract.Assert(m_count >= 0, "Count should never go below 0");
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+
+ /// <summary>
+ /// Returns whether we should notify the debugger of a wait completion. This returns true
+ /// iff at least one constituent task has its bit set.
+ /// </summary>
+ internal override bool ShouldNotifyDebuggerOfWaitCompletion
+ {
+ get
+ {
+ return
+ base.ShouldNotifyDebuggerOfWaitCompletion &&
+ Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
+ }
+ }
+ }
+ #endregion
+
+ #region WhenAny
+ /// <summary>
+ /// Creates a task that will complete when any of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
+ /// <remarks>
+ /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
+ /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> array contained a null task, or was empty.
+ /// </exception>
+ public static Task<Task> WhenAny(params Task[] tasks)
+ {
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ if (tasks.Length == 0)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
+ }
+ Contract.EndContractBlock();
+
+ // Make a defensive copy, as the user may manipulate the tasks array
+ // after we return but before the WhenAny asynchronously completes.
+ int taskCount = tasks.Length;
+ Task[] tasksCopy = new Task[taskCount];
+ for (int i = 0; i < taskCount; i++)
+ {
+ Task task = tasks[i];
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ tasksCopy[i] = task;
+ }
+
+ // Previously implemented CommonCWAnyLogic() can handle the rest
+ return TaskFactory.CommonCWAnyLogic(tasksCopy);
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when any of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
+ /// <remarks>
+ /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
+ /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> collection contained a null task, or was empty.
+ /// </exception>
+ public static Task<Task> WhenAny(IEnumerable<Task> tasks)
+ {
+ if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
+ Contract.EndContractBlock();
+
+ // Make a defensive copy, as the user may manipulate the tasks collection
+ // after we return but before the WhenAny asynchronously completes.
+ List<Task> taskList = new List<Task>();
+ foreach (Task task in tasks)
+ {
+ if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
+ taskList.Add(task);
+ }
+
+ if (taskList.Count == 0)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
+ }
+
+ // Previously implemented CommonCWAnyLogic() can handle the rest
+ return TaskFactory.CommonCWAnyLogic(taskList);
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when any of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
+ /// <remarks>
+ /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
+ /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> array contained a null task, or was empty.
+ /// </exception>
+ public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks)
+ {
+ // We would just like to do this:
+ // return (Task<Task<TResult>>) WhenAny( (Task[]) tasks);
+ // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
+
+ // Call WhenAny(Task[]) for basic functionality
+ Task<Task> intermediate = WhenAny((Task[])tasks);
+
+ // Return a continuation task with the correct result type
+ return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
+ TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ /// <summary>
+ /// Creates a task that will complete when any of the supplied tasks have completed.
+ /// </summary>
+ /// <param name="tasks">The tasks to wait on for completion.</param>
+ /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
+ /// <remarks>
+ /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
+ /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="tasks"/> argument was null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="tasks"/> collection contained a null task, or was empty.
+ /// </exception>
+ public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks)
+ {
+ // We would just like to do this:
+ // return (Task<Task<TResult>>) WhenAny( (IEnumerable<Task>) tasks);
+ // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
+
+ // Call WhenAny(IEnumerable<Task>) for basic functionality
+ Task<Task> intermediate = WhenAny((IEnumerable<Task>)tasks);
+
+ // Return a continuation task with the correct result type
+ return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
+ TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+ #endregion
+
+ [FriendAccessAllowed]
+ internal static Task<TResult> CreateUnwrapPromise<TResult>(Task outerTask, bool lookForOce)
+ {
+ Contract.Requires(outerTask != null);
+
+ return new UnwrapPromise<TResult>(outerTask, lookForOce);
+ }
+
+ internal virtual Delegate[] GetDelegateContinuationsForDebugger()
+ {
+ //Avoid an infinite loop by making sure the continuation object is not a reference to istelf.
+ if (this.m_continuationObject != this)
+ return GetDelegatesFromContinuationObject(this.m_continuationObject);
+ else
+ return null;
+ }
+
+ internal static Delegate[] GetDelegatesFromContinuationObject(object continuationObject)
+ {
+ if (continuationObject != null)
+ {
+ Action singleAction = continuationObject as Action;
+ if (singleAction != null)
+ {
+ return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(singleAction) };
+ }
+
+ TaskContinuation taskContinuation = continuationObject as TaskContinuation;
+ if (taskContinuation != null)
+ {
+ return taskContinuation.GetDelegateContinuationsForDebugger();
+ }
+
+ Task continuationTask = continuationObject as Task;
+ if (continuationTask != null)
+ {
+ Contract.Assert(continuationTask.m_action == null);
+ Delegate[] delegates = continuationTask.GetDelegateContinuationsForDebugger();
+ if (delegates != null)
+ return delegates;
+ }
+
+ //We need this ITaskCompletionAction after the Task because in the case of UnwrapPromise
+ //the VS debugger is more interested in the continuation than the internal invoke()
+ ITaskCompletionAction singleCompletionAction = continuationObject as ITaskCompletionAction;
+ if (singleCompletionAction != null)
+ {
+ return new Delegate[] { new Action<Task>(singleCompletionAction.Invoke) };
+ }
+
+ List<object> continuationList = continuationObject as List<object>;
+ if (continuationList != null)
+ {
+ List<Delegate> result = new List<Delegate>();
+ foreach (object obj in continuationList)
+ {
+ var innerDelegates = GetDelegatesFromContinuationObject(obj);
+ if (innerDelegates != null)
+ {
+ foreach (var del in innerDelegates)
+ {
+ if (del != null)
+ result.Add(del);
+ }
+ }
+ }
+
+ return result.ToArray();
+ }
+ }
+
+ return null;
+ }
+
+ private static Task GetActiveTaskFromId(int taskId)
+ {
+ Task task = null;
+ s_currentActiveTasks.TryGetValue(taskId, out task);
+ return task;
+ }
+
+ private static Task[] GetActiveTasks()
+ {
+
+ return new List<Task>(s_currentActiveTasks.Values).ToArray();
+ }
+
+
+ }
+
+ internal sealed class CompletionActionInvoker : IThreadPoolWorkItem
+ {
+ private readonly ITaskCompletionAction m_action;
+ private readonly Task m_completingTask;
+
+ internal CompletionActionInvoker(ITaskCompletionAction action, Task completingTask)
+ {
+ m_action = action;
+ m_completingTask = completingTask;
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+ m_action.Invoke(m_completingTask);
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
+ {
+ /* NOP */
+ }
+ }
+
+ // Proxy class for better debugging experience
+ internal class SystemThreadingTasks_TaskDebugView
+ {
+ private Task m_task;
+
+ public SystemThreadingTasks_TaskDebugView(Task task)
+ {
+ m_task = task;
+ }
+
+ public object AsyncState { get { return m_task.AsyncState; } }
+ public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } }
+ public Exception Exception { get { return m_task.Exception; } }
+ public int Id { get { return m_task.Id; } }
+ public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } }
+ public TaskStatus Status { get { return m_task.Status; } }
+ }
+
+ // Special purpose derivation of Task that supports limited replication through
+ // overriding the ShouldReplicate() method. This is used by the Parallel.For/ForEach
+ // methods.
+ internal class ParallelForReplicatingTask : Task
+ {
+ // Member variables
+ private int m_replicationDownCount; // downcounter to control replication
+
+ //
+ // Constructors
+ //
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ internal ParallelForReplicatingTask(
+ ParallelOptions parallelOptions, Action action, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions)
+ : base(action, null, Task.InternalCurrent, default(CancellationToken), creationOptions, internalOptions | InternalTaskOptions.SelfReplicating, null)
+ {
+ // Compute the down count based on scheduler/DOP info in parallelOptions.
+ m_replicationDownCount = parallelOptions.EffectiveMaxConcurrencyLevel;
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+
+ // Controls degree of replication. If downcounter is initialized to -1, then
+ // replication will be allowed to "run wild". Otherwise, this method decrements
+ // the downcounter each time it is called, calling false when it is called with
+ // a zero downcounter. This method returning false effectively ends the replication
+ // of the associated ParallelForReplicatingTask.
+ internal override bool ShouldReplicate()
+ {
+ if (m_replicationDownCount == -1) return true; // "run wild"
+
+ if (m_replicationDownCount > 0) // Decrement and return true if not called with 0 downcount
+ {
+ m_replicationDownCount--;
+ return true;
+ }
+
+ return false; // We're done replicating
+ }
+
+ internal override Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
+ TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
+ {
+ return new ParallelForReplicaTask(taskReplicaDelegate, stateObject, parentTask, taskScheduler,
+ creationOptionsForReplica, internalOptionsForReplica);
+ }
+
+
+ }
+
+ internal class ParallelForReplicaTask : Task
+ {
+ internal object m_stateForNextReplica; // some replicas may quit prematurely, in which case they will use this variable
+ // to save state they want to be picked up by the next replica queued to the same thread
+
+ internal object m_stateFromPreviousReplica; // some replicas may quit prematurely, in which case they will use this variable
+ // to save state they want to be picked up by the next replica queued to the same thread
+
+ internal Task m_handedOverChildReplica; // some replicas may quit prematurely, in which case they will use this variable
+ // to hand over the child replica they had queued to the next task that will replace them
+
+ internal ParallelForReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
+ TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica) :
+ base(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken), creationOptionsForReplica, internalOptionsForReplica, taskScheduler)
+ {
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
+ internal override Object SavedStateForNextReplica
+ {
+ get { return m_stateForNextReplica; }
+
+ set { m_stateForNextReplica = value; }
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
+ internal override Object SavedStateFromPreviousReplica
+ {
+ get { return m_stateFromPreviousReplica; }
+
+ set { m_stateFromPreviousReplica = value; }
+ }
+
+ // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
+ // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
+ internal override Task HandedOverChildReplica
+ {
+ get { return m_handedOverChildReplica; }
+
+ set { m_handedOverChildReplica = value; }
+ }
+ }
+
+ /// <summary>
+ /// Specifies flags that control optional behavior for the creation and execution of tasks.
+ /// </summary>
+ // NOTE: These options are a subset of TaskContinuationsOptions, thus before adding a flag check it is
+ // not already in use.
+ [Flags]
+ [Serializable]
+ public enum TaskCreationOptions
+ {
+ /// <summary>
+ /// Specifies that the default behavior should be used.
+ /// </summary>
+ None = 0x0,
+
+ /// <summary>
+ /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
+ /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
+ /// be run sooner, and tasks scheduled later will be more likely to be run later.
+ /// </summary>
+ PreferFairness = 0x01,
+
+ /// <summary>
+ /// Specifies that a task will be a long-running, course-grained operation. It provides a hint to the
+ /// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that oversubscription may be
+ /// warranted.
+ /// </summary>
+ LongRunning = 0x02,
+
+ /// <summary>
+ /// Specifies that a task is attached to a parent in the task hierarchy.
+ /// </summary>
+ AttachedToParent = 0x04,
+
+ /// <summary>
+ /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
+ /// </summary>
+ DenyChildAttach = 0x08,
+
+ /// <summary>
+ /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
+ /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
+ /// </summary>
+ HideScheduler = 0x10,
+
+ // 0x20 is already being used in TaskContinuationOptions
+
+ /// <summary>
+ /// Forces continuations added to the current task to be executed asynchronously.
+ /// This option has precedence over TaskContinuationOptions.ExecuteSynchronously
+ /// </summary>
+ RunContinuationsAsynchronously = 0x40
+ }
+
+
+ /// <summary>
+ /// Task creation flags which are only used internally.
+ /// </summary>
+ [Flags]
+ [Serializable]
+ internal enum InternalTaskOptions
+ {
+ /// <summary> Specifies "No internal task options" </summary>
+ None,
+
+ /// <summary>Used to filter out internal vs. public task creation options.</summary>
+ InternalOptionsMask = 0x0000FF00,
+
+ ChildReplica = 0x0100,
+ ContinuationTask = 0x0200,
+ PromiseTask = 0x0400,
+ SelfReplicating = 0x0800,
+
+ /// <summary>
+ /// Store the presence of TaskContinuationOptions.LazyCancellation, since it does not directly
+ /// translate into any TaskCreationOptions.
+ /// </summary>
+ LazyCancellation = 0x1000,
+
+ /// <summary>Specifies that the task will be queued by the runtime before handing it over to the user.
+ /// This flag will be used to skip the cancellationtoken registration step, which is only meant for unstarted tasks.</summary>
+ QueuedByRuntime = 0x2000,
+
+ /// <summary>
+ /// Denotes that Dispose should be a complete nop for a Task. Used when constructing tasks that are meant to be cached/reused.
+ /// </summary>
+ DoNotDispose = 0x4000
+ }
+
+ /// <summary>
+ /// Specifies flags that control optional behavior for the creation and execution of continuation tasks.
+ /// </summary>
+ [Flags]
+ [Serializable]
+ public enum TaskContinuationOptions
+ {
+ /// <summary>
+ /// Default = "Continue on any, no task options, run asynchronously"
+ /// Specifies that the default behavior should be used. Continuations, by default, will
+ /// be scheduled when the antecedent task completes, regardless of the task's final <see
+ /// cref="System.Threading.Tasks.TaskStatus">TaskStatus</see>.
+ /// </summary>
+ None = 0,
+
+ // These are identical to their meanings and values in TaskCreationOptions
+
+ /// <summary>
+ /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
+ /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
+ /// be run sooner, and tasks scheduled later will be more likely to be run later.
+ /// </summary>
+ PreferFairness = 0x01,
+
+ /// <summary>
+ /// Specifies that a task will be a long-running, course-grained operation. It provides
+ /// a hint to the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that
+ /// oversubscription may be warranted.
+ /// </summary>
+ LongRunning = 0x02,
+ /// <summary>
+ /// Specifies that a task is attached to a parent in the task hierarchy.
+ /// </summary>
+ AttachedToParent = 0x04,
+
+ /// <summary>
+ /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
+ /// </summary>
+ DenyChildAttach = 0x08,
+ /// <summary>
+ /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
+ /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
+ /// </summary>
+ HideScheduler = 0x10,
+
+ /// <summary>
+ /// In the case of continuation cancellation, prevents completion of the continuation until the antecedent has completed.
+ /// </summary>
+ LazyCancellation = 0x20,
+
+ RunContinuationsAsynchronously = 0x40,
+
+ // These are specific to continuations
+
+ /// <summary>
+ /// Specifies that the continuation task should not be scheduled if its antecedent ran to completion.
+ /// This option is not valid for multi-task continuations.
+ /// </summary>
+ NotOnRanToCompletion = 0x10000,
+ /// <summary>
+ /// Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled
+ /// exception. This option is not valid for multi-task continuations.
+ /// </summary>
+ NotOnFaulted = 0x20000,
+ /// <summary>
+ /// Specifies that the continuation task should not be scheduled if its antecedent was canceled. This
+ /// option is not valid for multi-task continuations.
+ /// </summary>
+ NotOnCanceled = 0x40000,
+ /// <summary>
+ /// Specifies that the continuation task should be scheduled only if its antecedent ran to
+ /// completion. This option is not valid for multi-task continuations.
+ /// </summary>
+ OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
+ /// <summary>
+ /// Specifies that the continuation task should be scheduled only if its antecedent threw an
+ /// unhandled exception. This option is not valid for multi-task continuations.
+ /// </summary>
+ OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
+ /// <summary>
+ /// Specifies that the continuation task should be scheduled only if its antecedent was canceled.
+ /// This option is not valid for multi-task continuations.
+ /// </summary>
+ OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
+ /// <summary>
+ /// Specifies that the continuation task should be executed synchronously. With this option
+ /// specified, the continuation will be run on the same thread that causes the antecedent task to
+ /// transition into its final state. If the antecedent is already complete when the continuation is
+ /// created, the continuation will run on the thread creating the continuation. Only very
+ /// short-running continuations should be executed synchronously.
+ /// </summary>
+ ExecuteSynchronously = 0x80000
+ }
+
+ /// <summary>
+ /// Internal helper class to keep track of stack depth and decide whether we should inline or not.
+ /// </summary>
+ internal class StackGuard
+ {
+ // current thread's depth of nested inline task executions
+ private int m_inliningDepth = 0;
+
+ // For relatively small inlining depths we don't want to get into the business of stack probing etc.
+ // This clearly leaves a window of opportunity for the user code to SO. However a piece of code
+ // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway.
+ private const int MAX_UNCHECKED_INLINING_DEPTH = 20;
+
+#if !FEATURE_CORECLR
+
+ private UInt64 m_lastKnownWatermark;
+ private static int s_pageSize;
+
+ // We are conservative here. We assume that the platform needs a whole 64KB to
+ // respond to stack overflow. This means that for very small stacks (e.g. 128KB)
+ // we'll fail a lot of stack checks incorrectly.
+ private const long STACK_RESERVED_SPACE = 4096 * 16;
+
+#endif // !FEATURE_CORECLR
+
+ /// <summary>
+ /// This method needs to be called before attempting inline execution on the current thread.
+ /// If false is returned, it means we are too close to the end of the stack and should give up inlining.
+ /// Each call to TryBeginInliningScope() that returns true must be matched with a
+ /// call to EndInliningScope() regardless of whether inlining actually took place.
+ /// </summary>
+ [SecuritySafeCritical]
+ internal bool TryBeginInliningScope()
+ {
+ // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls
+ if (m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || CheckForSufficientStack())
+ {
+ m_inliningDepth++;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// This needs to be called once for each previous successful TryBeginInliningScope() call after
+ /// inlining related logic runs.
+ /// </summary>
+ internal void EndInliningScope()
+ {
+ m_inliningDepth--;
+ Contract.Assert(m_inliningDepth >= 0, "Inlining depth count should never go negative.");
+
+ // do the right thing just in case...
+ if (m_inliningDepth < 0) m_inliningDepth = 0;
+ }
+
+ [SecurityCritical]
+ private unsafe bool CheckForSufficientStack()
+ {
+#if FEATURE_CORECLR
+ return RuntimeHelpers.TryEnsureSufficientExecutionStack();
+#else
+ // see if we already have the system page size info recorded
+ int pageSize = s_pageSize;
+ if (pageSize == 0)
+ {
+ // If not we need to query it from GetSystemInfo()
+ // Note that this happens only once for the process lifetime
+ Win32Native.SYSTEM_INFO sysInfo = new Win32Native.SYSTEM_INFO();
+ Win32Native.GetSystemInfo(ref sysInfo);
+
+ s_pageSize = pageSize = sysInfo.dwPageSize;
+ }
+
+ Win32Native.MEMORY_BASIC_INFORMATION stackInfo = new Win32Native.MEMORY_BASIC_INFORMATION();
+
+ // We subtract one page for our request. VirtualQuery rounds UP to the next page.
+ // Unfortunately, the stack grows down. If we're on the first page (last page in the
+ // VirtualAlloc), we'll be moved to the next page, which is off the stack!
+
+ UIntPtr currentAddr = new UIntPtr(&stackInfo - pageSize);
+ UInt64 current64 = currentAddr.ToUInt64();
+
+ // Check whether we previously recorded a deeper stack than where we currently are,
+ // If so we don't need to do the P/Invoke to VirtualQuery
+ if (m_lastKnownWatermark != 0 && current64 > m_lastKnownWatermark)
+ return true;
+
+ // Actual stack probe. P/Invoke to query for the current stack allocation information.
+ Win32Native.VirtualQuery(currentAddr.ToPointer(), ref stackInfo, (UIntPtr)(sizeof(Win32Native.MEMORY_BASIC_INFORMATION)));
+
+ // If the current address minus the base (remember: the stack grows downward in the
+ // address space) is greater than the number of bytes requested plus the reserved
+ // space at the end, the request has succeeded.
+
+ if ((current64 - ((UIntPtr)stackInfo.AllocationBase).ToUInt64()) > STACK_RESERVED_SPACE)
+ {
+ m_lastKnownWatermark = current64;
+ return true;
+ }
+
+ return false;
+#endif
+ }
+ }
+
+ // Special internal struct that we use to signify that we are not interested in
+ // a Task<VoidTaskResult>'s result.
+ internal struct VoidTaskResult { }
+
+ // Interface to which all completion actions must conform.
+ // This interface allows us to combine functionality and reduce allocations.
+ // For example, see Task.SetOnInvokeMres, and its use in Task.SpinThenBlockingWait().
+ // This code:
+ // ManualResetEvent mres = new ManualResetEventSlim(false, 0);
+ // Action<Task> completionAction = delegate { mres.Set() ; };
+ // AddCompletionAction(completionAction);
+ // gets replaced with this:
+ // SetOnInvokeMres mres = new SetOnInvokeMres();
+ // AddCompletionAction(mres);
+ // For additional examples of where this is used, see internal classes Task.SignalOnInvokeCDE,
+ // Task.WhenAllPromise, Task.WhenAllPromise<T>, TaskFactory.CompleteOnCountdownPromise,
+ // TaskFactory.CompleteOnCountdownPromise<T>, and TaskFactory.CompleteOnInvokePromise.
+ internal interface ITaskCompletionAction
+ {
+ /// <summary>Invoked to run the completion action.</summary>
+ void Invoke(Task completingTask);
+
+ /// <summary>
+ /// Some completion actions are considered internal implementation details of tasks,
+ /// using the continuation mechanism only for performance reasons. Such actions perform
+ /// known quantities and types of work, and can be invoked safely as a continuation even
+ /// if the system wants to prevent arbitrary continuations from running synchronously.
+ /// This should only return false for a limited set of implementations where a small amount
+ /// of work is guaranteed to be performed, e.g. setting a ManualResetEventSlim.
+ /// </summary>
+ bool InvokeMayRunArbitraryCode { get; }
+ }
+
+ // This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction,
+ // which minimizes the allocations needed for queuing it to its antecedent. This
+ // logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods.
+ internal sealed class UnwrapPromise<TResult> : Task<TResult>, ITaskCompletionAction
+ {
+ // The possible states for our UnwrapPromise, used by Invoke() to determine which logic to execute
+ private const byte STATE_WAITING_ON_OUTER_TASK = 0; // Invoke() means "process completed outer task"
+ private const byte STATE_WAITING_ON_INNER_TASK = 1; // Invoke() means "process completed inner task"
+ private const byte STATE_DONE = 2; // Invoke() means "something went wrong and we are hosed!"
+
+ // Keep track of our state; initialized to STATE_WAITING_ON_OUTER_TASK in the constructor
+ private byte _state;
+
+ // "Should we check for OperationCanceledExceptions on the outer task and interpret them as proxy cancellation?"
+ // Unwrap() sets this to false, Run() sets it to true.
+ private readonly bool _lookForOce;
+
+ public UnwrapPromise(Task outerTask, bool lookForOce)
+ : base((object)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent)
+ {
+ Contract.Requires(outerTask != null, "Expected non-null outerTask");
+ _lookForOce = lookForOce;
+ _state = STATE_WAITING_ON_OUTER_TASK;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Unwrap", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+
+ // Link ourselves to the outer task.
+ // If the outer task has already completed, take the fast path
+ // of immediately transferring its results or processing the inner task.
+ if (outerTask.IsCompleted)
+ {
+ ProcessCompletedOuterTask(outerTask);
+ }
+ else // Otherwise, process its completion asynchronously.
+ {
+ outerTask.AddCompletionAction(this);
+ }
+ }
+
+ // For ITaskCompletionAction
+ public void Invoke(Task completingTask)
+ {
+ // Check the current stack guard. If we're ok to inline,
+ // process the task, and reset the guard when we're done.
+ var sg = Task.CurrentStackGuard;
+ if (sg.TryBeginInliningScope())
+ {
+ try { InvokeCore(completingTask); }
+ finally { sg.EndInliningScope(); }
+ }
+ // Otherwise, we're too deep on the stack, and
+ // we shouldn't run the continuation chain here, so queue a work
+ // item to call back here to Invoke asynchronously.
+ else InvokeCoreAsync(completingTask);
+ }
+
+ /// <summary>
+ /// Processes the completed task. InvokeCore could be called twice:
+ /// once for the outer task, once for the inner task.
+ /// </summary>
+ /// <param name="completingTask">The completing outer or inner task.</param>
+ private void InvokeCore(Task completingTask)
+ {
+ switch (_state)
+ {
+ case STATE_WAITING_ON_OUTER_TASK:
+ ProcessCompletedOuterTask(completingTask);
+ // We bump the state inside of ProcessCompletedOuterTask because it can also be called from the constructor.
+ break;
+ case STATE_WAITING_ON_INNER_TASK:
+ bool result = TrySetFromTask(completingTask, lookForOce: false);
+ _state = STATE_DONE; // bump the state
+ Contract.Assert(result, "Expected TrySetFromTask from inner task to succeed");
+ break;
+ default:
+ Contract.Assert(false, "UnwrapPromise in illegal state");
+ break;
+ }
+ }
+
+ // Calls InvokeCore asynchronously.
+ [SecuritySafeCritical]
+ private void InvokeCoreAsync(Task completingTask)
+ {
+ // Queue a call to Invoke. If we're so deep on the stack that we're at risk of overflowing,
+ // there's a high liklihood this thread is going to be doing lots more work before
+ // returning to the thread pool (at the very least unwinding through thousands of
+ // stack frames). So we queue to the global queue.
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ // InvokeCore(completingTask);
+ var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state;
+ tuple.Item1.InvokeCore(tuple.Item2);
+ }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask));
+ }
+
+ /// <summary>Processes the outer task once it's completed.</summary>
+ /// <param name="task">The now-completed outer task.</param>
+ private void ProcessCompletedOuterTask(Task task)
+ {
+ Contract.Requires(task != null && task.IsCompleted, "Expected non-null, completed outer task");
+ Contract.Assert(_state == STATE_WAITING_ON_OUTER_TASK, "We're in the wrong state!");
+
+ // Bump our state before proceeding any further
+ _state = STATE_WAITING_ON_INNER_TASK;
+
+ switch (task.Status)
+ {
+ // If the outer task did not complete successfully, then record the
+ // cancellation/fault information to tcs.Task.
+ case TaskStatus.Canceled:
+ case TaskStatus.Faulted:
+ bool result = TrySetFromTask(task, _lookForOce);
+ Contract.Assert(result, "Expected TrySetFromTask from outer task to succeed");
+ break;
+
+ // Otherwise, process the inner task it returned.
+ case TaskStatus.RanToCompletion:
+ var taskOfTaskOfTResult = task as Task<Task<TResult>>; // it's either a Task<Task> or Task<Task<TResult>>
+ ProcessInnerTask(taskOfTaskOfTResult != null ?
+ taskOfTaskOfTResult.Result : ((Task<Task>)task).Result);
+ break;
+ }
+ }
+
+ /// <summary>Transfer the completion status from "task" to ourself.</summary>
+ /// <param name="task">The source task whose results should be transfered to <paramref name="promise"/>.</param>
+ /// <param name="lookForOce">Whether or not to look for OperationCanceledExceptions in task's exceptions if it faults.</param>
+ /// <returns>true if the transfer was successful; otherwise, false.</returns>
+ private bool TrySetFromTask(Task task, bool lookForOce)
+ {
+ Contract.Requires(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
+
+ bool result = false;
+ switch (task.Status)
+ {
+ case TaskStatus.Canceled:
+ result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
+ break;
+
+ case TaskStatus.Faulted:
+ var edis = task.GetExceptionDispatchInfos();
+ ExceptionDispatchInfo oceEdi;
+ OperationCanceledException oce;
+ if (lookForOce && edis.Count > 0 &&
+ (oceEdi = edis[0]) != null &&
+ (oce = oceEdi.SourceException as OperationCanceledException) != null)
+ {
+ result = TrySetCanceled(oce.CancellationToken, oceEdi);
+ }
+ else
+ {
+ result = TrySetException(edis);
+ }
+ break;
+
+ case TaskStatus.RanToCompletion:
+ var taskTResult = task as Task<TResult>;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+
+ result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
+ break;
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Processes the inner task of a Task{Task} or Task{Task{TResult}},
+ /// transferring the appropriate results to ourself.
+ /// </summary>
+ /// <param name="task">The inner task returned by the task provided by the user.</param>
+ private void ProcessInnerTask(Task task)
+ {
+ // If the inner task is null, the proxy should be canceled.
+ if (task == null)
+ {
+ TrySetCanceled(default(CancellationToken));
+ _state = STATE_DONE; // ... and record that we are done
+ }
+
+ // Fast path for if the inner task is already completed
+ else if (task.IsCompleted)
+ {
+ TrySetFromTask(task, lookForOce: false);
+ _state = STATE_DONE; // ... and record that we are done
+ }
+
+ // The inner task exists but is not yet complete, so when it does complete,
+ // take some action to set our completion state.
+ else
+ {
+ task.AddCompletionAction(this);
+ // We'll record that we are done when Invoke() is called.
+ }
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs
new file mode 100644
index 0000000000..f15e3e783a
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// An exception for task cancellations.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading.Tasks
+{
+
+ /// <summary>
+ /// Represents an exception used to communicate task cancellation.
+ /// </summary>
+ [Serializable]
+ public class TaskCanceledException : OperationCanceledException
+ {
+
+ [NonSerialized]
+ private Task m_canceledTask; // The task which has been canceled.
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class.
+ /// </summary>
+ public TaskCanceledException() : base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage"))
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with a specified error message.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ public TaskCanceledException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with a specified error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskCanceledException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class
+ /// with a reference to the <see cref="T:System.Threading.Tasks.Task"/> that has been canceled.
+ /// </summary>
+ /// <param name="task">A task that has been canceled.</param>
+ public TaskCanceledException(Task task) :
+ base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage"), task!=null ? task.CancellationToken:new CancellationToken())
+ {
+ m_canceledTask = task;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with serialized data.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination. </param>
+ protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ /// <summary>
+ /// Gets the task associated with this exception.
+ /// </summary>
+ /// <remarks>
+ /// It is permissible for no Task to be associated with a
+ /// <see cref="T:System.Threading.Tasks.TaskCanceledException"/>, in which case
+ /// this property will return null.
+ /// </remarks>
+ public Task Task
+ {
+ get { return m_canceledTask; }
+ }
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs
new file mode 100644
index 0000000000..8b1dd2a62f
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs
@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// TaskCompletionSource<TResult> is the producer end of an unbound future. Its
+// Task member may be distributed as the consumer end of the future.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics.Contracts;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using System.Security.Permissions;
+using System.Threading;
+
+// Disable the "reference to volatile field not treated as volatile" error.
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Represents the producer side of a <see cref="T:System.Threading.Tasks.Task{TResult}"/> unbound to a
+ /// delegate, providing access to the consumer side through the <see cref="Task"/> property.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// It is often the case that a <see cref="T:System.Threading.Tasks.Task{TResult}"/> is desired to
+ /// represent another asynchronous operation.
+ /// <see cref="TaskCompletionSource{TResult}">TaskCompletionSource</see> is provided for this purpose. It enables
+ /// the creation of a task that can be handed out to consumers, and those consumers can use the members
+ /// of the task as they would any other. However, unlike most tasks, the state of a task created by a
+ /// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the
+ /// completion of the external asynchronous operation to be propagated to the underlying Task. The
+ /// separation also ensures that consumers are not able to transition the state without access to the
+ /// corresponding TaskCompletionSource.
+ /// </para>
+ /// <para>
+ /// All members of <see cref="TaskCompletionSource{TResult}"/> are thread-safe
+ /// and may be used from multiple threads concurrently.
+ /// </para>
+ /// </remarks>
+ /// <typeparam name="TResult">The type of the result value assocatied with this <see
+ /// cref="TaskCompletionSource{TResult}"/>.</typeparam>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class TaskCompletionSource<TResult>
+ {
+ private readonly Task<TResult> m_task;
+
+ /// <summary>
+ /// Creates a <see cref="TaskCompletionSource{TResult}"/>.
+ /// </summary>
+ public TaskCompletionSource()
+ {
+ m_task = new Task<TResult>();
+ }
+
+ /// <summary>
+ /// Creates a <see cref="TaskCompletionSource{TResult}"/>
+ /// with the specified options.
+ /// </summary>
+ /// <remarks>
+ /// The <see cref="T:System.Threading.Tasks.Task{TResult}"/> created
+ /// by this instance and accessible through its <see cref="Task"/> property
+ /// will be instantiated using the specified <paramref name="creationOptions"/>.
+ /// </remarks>
+ /// <param name="creationOptions">The options to use when creating the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> represent options invalid for use
+ /// with a <see cref="TaskCompletionSource{TResult}"/>.
+ /// </exception>
+ public TaskCompletionSource(TaskCreationOptions creationOptions)
+ : this(null, creationOptions)
+ {
+ }
+
+ /// <summary>
+ /// Creates a <see cref="TaskCompletionSource{TResult}"/>
+ /// with the specified state.
+ /// </summary>
+ /// <param name="state">The state to use as the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>'s AsyncState.</param>
+ public TaskCompletionSource(object state)
+ : this(state, TaskCreationOptions.None)
+ {
+ }
+
+ /// <summary>
+ /// Creates a <see cref="TaskCompletionSource{TResult}"/> with
+ /// the specified state and options.
+ /// </summary>
+ /// <param name="creationOptions">The options to use when creating the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">The state to use as the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>'s AsyncState.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> represent options invalid for use
+ /// with a <see cref="TaskCompletionSource{TResult}"/>.
+ /// </exception>
+ public TaskCompletionSource(object state, TaskCreationOptions creationOptions)
+ {
+ m_task = new Task<TResult>(state, creationOptions);
+ }
+
+
+ /// <summary>
+ /// Gets the <see cref="T:System.Threading.Tasks.Task{TResult}"/> created
+ /// by this <see cref="TaskCompletionSource{TResult}"/>.
+ /// </summary>
+ /// <remarks>
+ /// This property enables a consumer access to the <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/> that is controlled by this instance.
+ /// The <see cref="SetResult"/>, <see cref="SetException(System.Exception)"/>,
+ /// <see cref="SetException(System.Collections.Generic.IEnumerable{System.Exception})"/>, and <see cref="SetCanceled"/>
+ /// methods (and their "Try" variants) on this instance all result in the relevant state
+ /// transitions on this underlying Task.
+ /// </remarks>
+ public Task<TResult> Task
+ {
+ get { return m_task; }
+ }
+
+ /// <summary>Spins until the underlying task is completed.</summary>
+ /// <remarks>This should only be called if the task is in the process of being completed by another thread.</remarks>
+ private void SpinUntilCompleted()
+ {
+ // Spin wait until the completion is finalized by another thread.
+ var sw = new SpinWait();
+ while (!m_task.IsCompleted)
+ sw.SpinOnce();
+ }
+
+ /// <summary>
+ /// Attempts to transition the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>
+ /// state.
+ /// </summary>
+ /// <param name="exception">The exception to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>True if the operation was successful; otherwise, false.</returns>
+ /// <remarks>This operation will return false if the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="exception"/> argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public bool TrySetException(Exception exception)
+ {
+ if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
+
+ bool rval = m_task.TrySetException(exception);
+ if (!rval && !m_task.IsCompleted) SpinUntilCompleted();
+ return rval;
+ }
+
+ /// <summary>
+ /// Attempts to transition the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>
+ /// state.
+ /// </summary>
+ /// <param name="exceptions">The collection of exceptions to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>True if the operation was successful; otherwise, false.</returns>
+ /// <remarks>This operation will return false if the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception>
+ /// <exception cref="T:System.ArgumentException">The <paramref name="exceptions"/> collection is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public bool TrySetException(IEnumerable<Exception> exceptions)
+ {
+ if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions);
+
+ List<Exception> defensiveCopy = new List<Exception>();
+ foreach (Exception e in exceptions)
+ {
+ if (e == null)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NullException, ExceptionArgument.exceptions);
+ defensiveCopy.Add(e);
+ }
+
+ if (defensiveCopy.Count == 0)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions, ExceptionArgument.exceptions);
+
+ bool rval = m_task.TrySetException(defensiveCopy);
+ if (!rval && !m_task.IsCompleted) SpinUntilCompleted();
+ return rval;
+ }
+
+ /// <summary>Attempts to transition the underlying task to the faulted state.</summary>
+ /// <param name="exceptions">The collection of exception dispatch infos to bind to this task.</param>
+ /// <returns>True if the operation was successful; otherwise, false.</returns>
+ /// <remarks>Unlike the public methods, this method doesn't currently validate that its arguments are correct.</remarks>
+ internal bool TrySetException(IEnumerable<ExceptionDispatchInfo> exceptions)
+ {
+ Contract.Assert(exceptions != null);
+#if DEBUG
+ foreach(var edi in exceptions) Contract.Assert(edi != null, "Contents must be non-null");
+#endif
+
+ bool rval = m_task.TrySetException(exceptions);
+ if (!rval && !m_task.IsCompleted) SpinUntilCompleted();
+ return rval;
+ }
+
+ /// <summary>
+ /// Transitions the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>
+ /// state.
+ /// </summary>
+ /// <param name="exception">The exception to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="exception"/> argument is null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public void SetException(Exception exception)
+ {
+ if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
+
+ if (!TrySetException(exception))
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
+ }
+ }
+
+ /// <summary>
+ /// Transitions the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>
+ /// state.
+ /// </summary>
+ /// <param name="exceptions">The collection of exceptions to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public void SetException(IEnumerable<Exception> exceptions)
+ {
+ if (!TrySetException(exceptions))
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
+ }
+ }
+
+
+ /// <summary>
+ /// Attempts to transition the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>
+ /// state.
+ /// </summary>
+ /// <param name="result">The result value to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>True if the operation was successful; otherwise, false.</returns>
+ /// <remarks>This operation will return false if the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public bool TrySetResult(TResult result)
+ {
+ bool rval = m_task.TrySetResult(result);
+ if (!rval && !m_task.IsCompleted) SpinUntilCompleted();
+ return rval;
+ }
+
+ /// <summary>
+ /// Transitions the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>
+ /// state.
+ /// </summary>
+ /// <param name="result">The result value to bind to this <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public void SetResult(TResult result)
+ {
+ if (!TrySetResult(result))
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
+ }
+
+ /// <summary>
+ /// Attempts to transition the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>
+ /// state.
+ /// </summary>
+ /// <returns>True if the operation was successful; otherwise, false.</returns>
+ /// <remarks>This operation will return false if the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </remarks>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public bool TrySetCanceled()
+ {
+ return TrySetCanceled(default(CancellationToken));
+ }
+
+ // Enables a token to be stored into the canceled task
+ public bool TrySetCanceled(CancellationToken cancellationToken)
+ {
+ bool rval = m_task.TrySetCanceled(cancellationToken);
+ if (!rval && !m_task.IsCompleted) SpinUntilCompleted();
+ return rval;
+ }
+
+ /// <summary>
+ /// Transitions the underlying
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>
+ /// state.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one
+ /// of the three final states:
+ /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+ /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+ /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+ public void SetCanceled()
+ {
+ if(!TrySetCanceled())
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
new file mode 100644
index 0000000000..4c035dfddb
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
@@ -0,0 +1,867 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Implementation of task continuations, TaskContinuation, and its descendants.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Security;
+using System.Diagnostics.Contracts;
+using System.Runtime.ExceptionServices;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+#if FEATURE_COMINTEROP
+using System.Runtime.InteropServices.WindowsRuntime;
+#endif // FEATURE_COMINTEROP
+
+namespace System.Threading.Tasks
+{
+ // Task type used to implement: Task ContinueWith(Action<Task,...>)
+ internal sealed class ContinuationTaskFromTask : Task
+ {
+ private Task m_antecedent;
+
+ public ContinuationTaskFromTask(
+ Task antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
+ base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
+ {
+ Contract.Requires(action is Action<Task> || action is Action<Task, object>,
+ "Invalid delegate type in ContinuationTaskFromTask");
+ m_antecedent = antecedent;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
+ /// </summary>
+ internal override void InnerInvoke()
+ {
+ // Get and null out the antecedent. This is crucial to avoid a memory
+ // leak with long chains of continuations.
+ var antecedent = m_antecedent;
+ Contract.Assert(antecedent != null,
+ "No antecedent was set for the ContinuationTaskFromTask.");
+ m_antecedent = null;
+
+ // Notify the debugger we're completing an asynchronous wait on a task
+ antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // Invoke the delegate
+ Contract.Assert(m_action != null);
+ var action = m_action as Action<Task>;
+ if (action != null)
+ {
+ action(antecedent);
+ return;
+ }
+ var actionWithState = m_action as Action<Task, object>;
+ if (actionWithState != null)
+ {
+ actionWithState(antecedent, m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in ContinuationTaskFromTask");
+ }
+ }
+
+ // Task type used to implement: Task<TResult> ContinueWith(Func<Task,...>)
+ internal sealed class ContinuationResultTaskFromTask<TResult> : Task<TResult>
+ {
+ private Task m_antecedent;
+
+ public ContinuationResultTaskFromTask(
+ Task antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
+ base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
+ {
+ Contract.Requires(function is Func<Task, TResult> || function is Func<Task, object, TResult>,
+ "Invalid delegate type in ContinuationResultTaskFromTask");
+ m_antecedent = antecedent;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
+ /// </summary>
+ internal override void InnerInvoke()
+ {
+ // Get and null out the antecedent. This is crucial to avoid a memory
+ // leak with long chains of continuations.
+ var antecedent = m_antecedent;
+ Contract.Assert(antecedent != null,
+ "No antecedent was set for the ContinuationResultTaskFromTask.");
+ m_antecedent = null;
+
+ // Notify the debugger we're completing an asynchronous wait on a task
+ antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // Invoke the delegate
+ Contract.Assert(m_action != null);
+ var func = m_action as Func<Task, TResult>;
+ if (func != null)
+ {
+ m_result = func(antecedent);
+ return;
+ }
+ var funcWithState = m_action as Func<Task, object, TResult>;
+ if (funcWithState != null)
+ {
+ m_result = funcWithState(antecedent, m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromTask");
+ }
+ }
+
+ // Task type used to implement: Task ContinueWith(Action<Task<TAntecedentResult>,...>)
+ internal sealed class ContinuationTaskFromResultTask<TAntecedentResult> : Task
+ {
+ private Task<TAntecedentResult> m_antecedent;
+
+ public ContinuationTaskFromResultTask(
+ Task<TAntecedentResult> antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
+ base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
+ {
+ Contract.Requires(action is Action<Task<TAntecedentResult>> || action is Action<Task<TAntecedentResult>, object>,
+ "Invalid delegate type in ContinuationTaskFromResultTask");
+ m_antecedent = antecedent;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
+ /// </summary>
+ internal override void InnerInvoke()
+ {
+ // Get and null out the antecedent. This is crucial to avoid a memory
+ // leak with long chains of continuations.
+ var antecedent = m_antecedent;
+ Contract.Assert(antecedent != null,
+ "No antecedent was set for the ContinuationTaskFromResultTask.");
+ m_antecedent = null;
+
+ // Notify the debugger we're completing an asynchronous wait on a task
+ antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // Invoke the delegate
+ Contract.Assert(m_action != null);
+ var action = m_action as Action<Task<TAntecedentResult>>;
+ if (action != null)
+ {
+ action(antecedent);
+ return;
+ }
+ var actionWithState = m_action as Action<Task<TAntecedentResult>, object>;
+ if (actionWithState != null)
+ {
+ actionWithState(antecedent, m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in ContinuationTaskFromResultTask");
+ }
+ }
+
+ // Task type used to implement: Task<TResult> ContinueWith(Func<Task<TAntecedentResult>,...>)
+ internal sealed class ContinuationResultTaskFromResultTask<TAntecedentResult, TResult> : Task<TResult>
+ {
+ private Task<TAntecedentResult> m_antecedent;
+
+ public ContinuationResultTaskFromResultTask(
+ Task<TAntecedentResult> antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
+ base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
+ {
+ Contract.Requires(function is Func<Task<TAntecedentResult>, TResult> || function is Func<Task<TAntecedentResult>, object, TResult>,
+ "Invalid delegate type in ContinuationResultTaskFromResultTask");
+ m_antecedent = antecedent;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
+ /// </summary>
+ internal override void InnerInvoke()
+ {
+ // Get and null out the antecedent. This is crucial to avoid a memory
+ // leak with long chains of continuations.
+ var antecedent = m_antecedent;
+ Contract.Assert(antecedent != null,
+ "No antecedent was set for the ContinuationResultTaskFromResultTask.");
+ m_antecedent = null;
+
+ // Notify the debugger we're completing an asynchronous wait on a task
+ antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // Invoke the delegate
+ Contract.Assert(m_action != null);
+ var func = m_action as Func<Task<TAntecedentResult>, TResult>;
+ if (func != null)
+ {
+ m_result = func(antecedent);
+ return;
+ }
+ var funcWithState = m_action as Func<Task<TAntecedentResult>, object, TResult>;
+ if (funcWithState != null)
+ {
+ m_result = funcWithState(antecedent, m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromResultTask");
+ }
+ }
+
+ // For performance reasons, we don't just have a single way of representing
+ // a continuation object. Rather, we have a hierarchy of types:
+ // - TaskContinuation: abstract base that provides a virtual Run method
+ // - StandardTaskContinuation: wraps a task,options,and scheduler, and overrides Run to process the task with that configuration
+ // - AwaitTaskContinuation: base for continuations created through TaskAwaiter; targets default scheduler by default
+ // - TaskSchedulerAwaitTaskContinuation: awaiting with a non-default TaskScheduler
+ // - SynchronizationContextAwaitTaskContinuation: awaiting with a "current" sync ctx
+
+ /// <summary>Represents a continuation.</summary>
+ internal abstract class TaskContinuation
+ {
+ /// <summary>Inlines or schedules the continuation.</summary>
+ /// <param name="completedTask">The antecedent task that has completed.</param>
+ /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
+ internal abstract void Run(Task completedTask, bool bCanInlineContinuationTask);
+
+ /// <summary>Tries to run the task on the current thread, if possible; otherwise, schedules it.</summary>
+ /// <param name="task">The task to run</param>
+ /// <param name="needsProtection">
+ /// true if we need to protect against multiple threads racing to start/cancel the task; otherwise, false.
+ /// </param>
+ [SecuritySafeCritical]
+ protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtection)
+ {
+ Contract.Requires(task != null);
+ Contract.Assert(task.m_taskScheduler != null);
+
+ // Set the TASK_STATE_STARTED flag. This only needs to be done
+ // if the task may be canceled or if someone else has a reference to it
+ // that may try to execute it.
+ if (needsProtection)
+ {
+ if (!task.MarkStarted())
+ return; // task has been previously started or canceled. Stop processing.
+ }
+ else
+ {
+ task.m_stateFlags |= Task.TASK_STATE_STARTED;
+ }
+
+ // Try to inline it but queue if we can't
+ try
+ {
+ if (!task.m_taskScheduler.TryRunInline(task, taskWasPreviouslyQueued: false))
+ {
+ task.m_taskScheduler.InternalQueueTask(task);
+ }
+ }
+ catch (Exception e)
+ {
+ // Either TryRunInline() or QueueTask() threw an exception. Record the exception, marking the task as Faulted.
+ // However if it was a ThreadAbortException coming from TryRunInline we need to skip here,
+ // because it would already have been handled in Task.Execute()
+ if (!(e is ThreadAbortException &&
+ (task.m_stateFlags & Task.TASK_STATE_THREAD_WAS_ABORTED) != 0)) // this ensures TAEs from QueueTask will be wrapped in TSE
+ {
+ TaskSchedulerException tse = new TaskSchedulerException(e);
+ task.AddException(tse);
+ task.Finish(false);
+ }
+
+ // Don't re-throw.
+ }
+ }
+
+ internal abstract Delegate[] GetDelegateContinuationsForDebugger();
+
+ }
+
+ /// <summary>Provides the standard implementation of a task continuation.</summary>
+ internal class StandardTaskContinuation : TaskContinuation
+ {
+ /// <summary>The unstarted continuation task.</summary>
+ internal readonly Task m_task;
+ /// <summary>The options to use with the continuation task.</summary>
+ internal readonly TaskContinuationOptions m_options;
+ /// <summary>The task scheduler with which to run the continuation task.</summary>
+ private readonly TaskScheduler m_taskScheduler;
+
+ /// <summary>Initializes a new continuation.</summary>
+ /// <param name="task">The task to be activated.</param>
+ /// <param name="options">The continuation options.</param>
+ /// <param name="scheduler">The scheduler to use for the continuation.</param>
+ internal StandardTaskContinuation(Task task, TaskContinuationOptions options, TaskScheduler scheduler)
+ {
+ Contract.Requires(task != null, "TaskContinuation ctor: task is null");
+ Contract.Requires(scheduler != null, "TaskContinuation ctor: scheduler is null");
+ m_task = task;
+ m_options = options;
+ m_taskScheduler = scheduler;
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, m_task.Id, "Task.ContinueWith: " + ((Delegate)task.m_action).Method.Name, 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ Task.AddToActiveTasks(m_task);
+ }
+ }
+
+ /// <summary>Invokes the continuation for the target completion task.</summary>
+ /// <param name="completedTask">The completed task.</param>
+ /// <param name="bCanInlineContinuationTask">Whether the continuation can be inlined.</param>
+ internal override void Run(Task completedTask, bool bCanInlineContinuationTask)
+ {
+ Contract.Assert(completedTask != null);
+ Contract.Assert(completedTask.IsCompleted, "ContinuationTask.Run(): completedTask not completed");
+
+ // Check if the completion status of the task works with the desired
+ // activation criteria of the TaskContinuationOptions.
+ TaskContinuationOptions options = m_options;
+ bool isRightKind =
+ completedTask.IsRanToCompletion ?
+ (options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
+ (completedTask.IsCanceled ?
+ (options & TaskContinuationOptions.NotOnCanceled) == 0 :
+ (options & TaskContinuationOptions.NotOnFaulted) == 0);
+
+ // If the completion status is allowed, run the continuation.
+ Task continuationTask = m_task;
+ if (isRightKind)
+ {
+ //If the task was cancel before running (e.g a ContinueWhenAll with a cancelled caancelation token)
+ //we will still flow it to ScheduleAndStart() were it will check the status before running
+ //We check here to avoid faulty logs that contain a join event to an operation that was already set as completed.
+ if (!continuationTask.IsCanceled && AsyncCausalityTracer.LoggingOn)
+ {
+ // Log now that we are sure that this continuation is being ran
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, continuationTask.Id, CausalityRelation.AssignDelegate);
+ }
+ continuationTask.m_taskScheduler = m_taskScheduler;
+
+ // Either run directly or just queue it up for execution, depending
+ // on whether synchronous or asynchronous execution is wanted.
+ if (bCanInlineContinuationTask && // inlining is allowed by the caller
+ (options & TaskContinuationOptions.ExecuteSynchronously) != 0) // synchronous execution was requested by the continuation's creator
+ {
+ InlineIfPossibleOrElseQueue(continuationTask, needsProtection: true);
+ }
+ else
+ {
+ try { continuationTask.ScheduleAndStart(needsProtection: true); }
+ catch (TaskSchedulerException)
+ {
+ // No further action is necessary -- ScheduleAndStart() already transitioned the
+ // task to faulted. But we want to make sure that no exception is thrown from here.
+ }
+ }
+ }
+ // Otherwise, the final state of this task does not match the desired
+ // continuation activation criteria; cancel it to denote this.
+ else continuationTask.InternalCancel(false);
+ }
+
+ internal override Delegate[] GetDelegateContinuationsForDebugger()
+ {
+ if (m_task.m_action == null)
+ {
+ return m_task.GetDelegateContinuationsForDebugger();
+ }
+
+ return new Delegate[] { m_task.m_action as Delegate };
+ }
+ }
+
+ /// <summary>Task continuation for awaiting with a current synchronization context.</summary>
+ internal sealed class SynchronizationContextAwaitTaskContinuation : AwaitTaskContinuation
+ {
+ /// <summary>SendOrPostCallback delegate to invoke the action.</summary>
+ private readonly static SendOrPostCallback s_postCallback = state => ((Action)state)(); // can't use InvokeAction as it's SecurityCritical
+ /// <summary>Cached delegate for PostAction</summary>
+ [SecurityCritical]
+ private static ContextCallback s_postActionCallback;
+ /// <summary>The context with which to run the action.</summary>
+ private readonly SynchronizationContext m_syncContext;
+
+ /// <summary>Initializes the SynchronizationContextAwaitTaskContinuation.</summary>
+ /// <param name="context">The synchronization context with which to invoke the action. Must not be null.</param>
+ /// <param name="action">The action to invoke. Must not be null.</param>
+ /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
+ /// <param name="stackMark">The captured stack mark.</param>
+ [SecurityCritical]
+ internal SynchronizationContextAwaitTaskContinuation(
+ SynchronizationContext context, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
+ base(action, flowExecutionContext, ref stackMark)
+ {
+ Contract.Assert(context != null);
+ m_syncContext = context;
+ }
+
+ /// <summary>Inlines or schedules the continuation.</summary>
+ /// <param name="ignored">The antecedent task, which is ignored.</param>
+ /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
+ [SecuritySafeCritical]
+ internal sealed override void Run(Task task, bool canInlineContinuationTask)
+ {
+ // If we're allowed to inline, run the action on this thread.
+ if (canInlineContinuationTask &&
+ m_syncContext == SynchronizationContext.CurrentNoFlow)
+ {
+ RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask);
+ }
+ // Otherwise, Post the action back to the SynchronizationContext.
+ else
+ {
+ TplEtwProvider etwLog = TplEtwProvider.Log;
+ if (etwLog.IsEnabled())
+ {
+ m_continuationId = Task.NewId();
+ etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
+ }
+ RunCallback(GetPostActionCallback(), this, ref Task.t_currentTask);
+ }
+ // Any exceptions will be handled by RunCallback.
+ }
+
+ /// <summary>Calls InvokeOrPostAction(false) on the supplied SynchronizationContextAwaitTaskContinuation.</summary>
+ /// <param name="state">The SynchronizationContextAwaitTaskContinuation.</param>
+ [SecurityCritical]
+ private static void PostAction(object state)
+ {
+ var c = (SynchronizationContextAwaitTaskContinuation)state;
+
+ TplEtwProvider etwLog = TplEtwProvider.Log;
+ if (etwLog.TasksSetActivityIds && c.m_continuationId != 0)
+ {
+ c.m_syncContext.Post(s_postCallback, GetActionLogDelegate(c.m_continuationId, c.m_action));
+ }
+ else
+ {
+ c.m_syncContext.Post(s_postCallback, c.m_action); // s_postCallback is manually cached, as the compiler won't in a SecurityCritical method
+ }
+ }
+
+ private static Action GetActionLogDelegate(int continuationId, Action action)
+ {
+ return () =>
+ {
+ Guid savedActivityId;
+ Guid activityId = TplEtwProvider.CreateGuidForTaskID(continuationId);
+ System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
+ try { action(); }
+ finally { System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId); }
+ };
+ }
+
+ /// <summary>Gets a cached delegate for the PostAction method.</summary>
+ /// <returns>
+ /// A delegate for PostAction, which expects a SynchronizationContextAwaitTaskContinuation
+ /// to be passed as state.
+ /// </returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [SecurityCritical]
+ private static ContextCallback GetPostActionCallback()
+ {
+ ContextCallback callback = s_postActionCallback;
+ if (callback == null) { s_postActionCallback = callback = PostAction; } // lazily initialize SecurityCritical delegate
+ return callback;
+ }
+ }
+
+ /// <summary>Task continuation for awaiting with a task scheduler.</summary>
+ internal sealed class TaskSchedulerAwaitTaskContinuation : AwaitTaskContinuation
+ {
+ /// <summary>The scheduler on which to run the action.</summary>
+ private readonly TaskScheduler m_scheduler;
+
+ /// <summary>Initializes the TaskSchedulerAwaitTaskContinuation.</summary>
+ /// <param name="scheduler">The task scheduler with which to invoke the action. Must not be null.</param>
+ /// <param name="action">The action to invoke. Must not be null.</param>
+ /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
+ /// <param name="stackMark">The captured stack mark.</param>
+ [SecurityCritical]
+ internal TaskSchedulerAwaitTaskContinuation(
+ TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
+ base(action, flowExecutionContext, ref stackMark)
+ {
+ Contract.Assert(scheduler != null);
+ m_scheduler = scheduler;
+ }
+
+ /// <summary>Inlines or schedules the continuation.</summary>
+ /// <param name="ignored">The antecedent task, which is ignored.</param>
+ /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
+ internal sealed override void Run(Task ignored, bool canInlineContinuationTask)
+ {
+ // If we're targeting the default scheduler, we can use the faster path provided by the base class.
+ if (m_scheduler == TaskScheduler.Default)
+ {
+ base.Run(ignored, canInlineContinuationTask);
+ }
+ else
+ {
+ // We permit inlining if the caller allows us to, and
+ // either we're on a thread pool thread (in which case we're fine running arbitrary code)
+ // or we're already on the target scheduler (in which case we'll just ask the scheduler
+ // whether it's ok to run here). We include the IsThreadPoolThread check here, whereas
+ // we don't in AwaitTaskContinuation.Run, since here it expands what's allowed as opposed
+ // to in AwaitTaskContinuation.Run where it restricts what's allowed.
+ bool inlineIfPossible = canInlineContinuationTask &&
+ (TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread);
+
+ // Create the continuation task task. If we're allowed to inline, try to do so.
+ // The target scheduler may still deny us from executing on this thread, in which case this'll be queued.
+ var task = CreateTask(state => {
+ try { ((Action)state)(); }
+ catch (Exception exc) { ThrowAsyncIfNecessary(exc); }
+ }, m_action, m_scheduler);
+
+ if (inlineIfPossible)
+ {
+ InlineIfPossibleOrElseQueue(task, needsProtection: false);
+ }
+ else
+ {
+ // We need to run asynchronously, so just schedule the task.
+ try { task.ScheduleAndStart(needsProtection: false); }
+ catch (TaskSchedulerException) { } // No further action is necessary, as ScheduleAndStart already transitioned task to faulted
+ }
+ }
+ }
+ }
+
+ /// <summary>Base task continuation class used for await continuations.</summary>
+ internal class AwaitTaskContinuation : TaskContinuation, IThreadPoolWorkItem
+ {
+ /// <summary>The ExecutionContext with which to run the continuation.</summary>
+ private readonly ExecutionContext m_capturedContext;
+ /// <summary>The action to invoke.</summary>
+ protected readonly Action m_action;
+
+ protected int m_continuationId;
+
+ /// <summary>Initializes the continuation.</summary>
+ /// <param name="action">The action to invoke. Must not be null.</param>
+ /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
+ /// <param name="stackMark">The captured stack mark with which to construct an ExecutionContext.</param>
+ [SecurityCritical]
+ internal AwaitTaskContinuation(Action action, bool flowExecutionContext, ref StackCrawlMark stackMark)
+ {
+ Contract.Requires(action != null);
+ m_action = action;
+ if (flowExecutionContext)
+ {
+ m_capturedContext = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
+ }
+ }
+
+ /// <summary>Initializes the continuation.</summary>
+ /// <param name="action">The action to invoke. Must not be null.</param>
+ /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
+ [SecurityCritical]
+ internal AwaitTaskContinuation(Action action, bool flowExecutionContext)
+ {
+ Contract.Requires(action != null);
+ m_action = action;
+ if (flowExecutionContext)
+ {
+ m_capturedContext = ExecutionContext.FastCapture();
+ }
+ }
+
+ /// <summary>Creates a task to run the action with the specified state on the specified scheduler.</summary>
+ /// <param name="action">The action to run. Must not be null.</param>
+ /// <param name="state">The state to pass to the action. Must not be null.</param>
+ /// <param name="scheduler">The scheduler to target.</param>
+ /// <returns>The created task.</returns>
+ protected Task CreateTask(Action<object> action, object state, TaskScheduler scheduler)
+ {
+ Contract.Requires(action != null);
+ Contract.Requires(scheduler != null);
+
+ return new Task(
+ action, state, null, default(CancellationToken),
+ TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler)
+ {
+ CapturedContext = m_capturedContext
+ };
+ }
+
+ /// <summary>Inlines or schedules the continuation onto the default scheduler.</summary>
+ /// <param name="ignored">The antecedent task, which is ignored.</param>
+ /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
+ [SecuritySafeCritical]
+ internal override void Run(Task task, bool canInlineContinuationTask)
+ {
+ // For the base AwaitTaskContinuation, we allow inlining if our caller allows it
+ // and if we're in a "valid location" for it. See the comments on
+ // IsValidLocationForInlining for more about what's valid. For performance
+ // reasons we would like to always inline, but we don't in some cases to avoid
+ // running arbitrary amounts of work in suspected "bad locations", like UI threads.
+ if (canInlineContinuationTask && IsValidLocationForInlining)
+ {
+ RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask); // any exceptions from m_action will be handled by s_callbackRunAction
+ }
+ else
+ {
+ TplEtwProvider etwLog = TplEtwProvider.Log;
+ if (etwLog.IsEnabled())
+ {
+ m_continuationId = Task.NewId();
+ etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
+ }
+
+ // We couldn't inline, so now we need to schedule it
+ ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the current thread is an appropriate location to inline a continuation's execution.
+ /// </summary>
+ /// <remarks>
+ /// Returns whether SynchronizationContext is null and we're in the default scheduler.
+ /// If the await had a SynchronizationContext/TaskScheduler where it began and the
+ /// default/ConfigureAwait(true) was used, then we won't be on this path. If, however,
+ /// ConfigureAwait(false) was used, or the SynchronizationContext and TaskScheduler were
+ /// naturally null/Default, then we might end up here. If we do, we need to make sure
+ /// that we don't execute continuations in a place that isn't set up to handle them, e.g.
+ /// running arbitrary amounts of code on the UI thread. It would be "correct", but very
+ /// expensive, to always run the continuations asynchronously, incurring lots of context
+ /// switches and allocations and locks and the like. As such, we employ the heuristic
+ /// that if the current thread has a non-null SynchronizationContext or a non-default
+ /// scheduler, then we better not run arbitrary continuations here.
+ /// </remarks>
+ internal static bool IsValidLocationForInlining
+ {
+ get
+ {
+ // If there's a SynchronizationContext, we'll be conservative and say
+ // this is a bad location to inline.
+ var ctx = SynchronizationContext.CurrentNoFlow;
+ if (ctx != null && ctx.GetType() != typeof(SynchronizationContext)) return false;
+
+ // Similarly, if there's a non-default TaskScheduler, we'll be conservative
+ // and say this is a bad location to inline.
+ var sched = TaskScheduler.InternalCurrent;
+ return sched == null || sched == TaskScheduler.Default;
+ }
+ }
+
+ /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary>
+ [SecurityCritical]
+ void ExecuteWorkItemHelper()
+ {
+ var etwLog = TplEtwProvider.Log;
+ Guid savedActivityId = Guid.Empty;
+ if (etwLog.TasksSetActivityIds && m_continuationId != 0)
+ {
+ Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
+ System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
+ }
+ try
+ {
+ // We're not inside of a task, so t_currentTask doesn't need to be specially maintained.
+ // We're on a thread pool thread with no higher-level callers, so exceptions can just propagate.
+
+ // If there's no execution context, just invoke the delegate.
+ if (m_capturedContext == null)
+ {
+ m_action();
+ }
+ // If there is an execution context, get the cached delegate and run the action under the context.
+ else
+ {
+ try
+ {
+ ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true);
+ }
+ finally { m_capturedContext.Dispose(); }
+ }
+ }
+ finally
+ {
+ if (etwLog.TasksSetActivityIds && m_continuationId != 0)
+ {
+ System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId);
+ }
+ }
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+ // inline the fast path
+ if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled()
+ )
+ {
+ m_action();
+ }
+ else
+ {
+ ExecuteWorkItemHelper();
+ }
+ }
+
+ /// <summary>
+ /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem.
+ /// </summary>
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
+
+ /// <summary>Cached delegate that invokes an Action passed as an object parameter.</summary>
+ [SecurityCritical]
+ private static ContextCallback s_invokeActionCallback;
+
+ /// <summary>Runs an action provided as an object parameter.</summary>
+ /// <param name="state">The Action to invoke.</param>
+ [SecurityCritical]
+ private static void InvokeAction(object state) { ((Action)state)(); }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [SecurityCritical]
+ protected static ContextCallback GetInvokeActionCallback()
+ {
+ ContextCallback callback = s_invokeActionCallback;
+ if (callback == null) { s_invokeActionCallback = callback = InvokeAction; } // lazily initialize SecurityCritical delegate
+ return callback;
+ }
+
+ /// <summary>Runs the callback synchronously with the provided state.</summary>
+ /// <param name="callback">The callback to run.</param>
+ /// <param name="state">The state to pass to the callback.</param>
+ /// <param name="currentTask">A reference to Task.t_currentTask.</param>
+ [SecurityCritical]
+ protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
+ {
+ Contract.Requires(callback != null);
+ Contract.Assert(currentTask == Task.t_currentTask);
+
+ // Pretend there's no current task, so that no task is seen as a parent
+ // and TaskScheduler.Current does not reflect false information
+ var prevCurrentTask = currentTask;
+ try
+ {
+ if (prevCurrentTask != null) currentTask = null;
+
+ // If there's no captured context, just run the callback directly.
+ if (m_capturedContext == null) callback(state);
+ // Otherwise, use the captured context to do so.
+ else ExecutionContext.Run(m_capturedContext, callback, state, true);
+ }
+ catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
+ {
+ ThrowAsyncIfNecessary(exc);
+ }
+ finally
+ {
+ // Restore the current task information
+ if (prevCurrentTask != null) currentTask = prevCurrentTask;
+
+ // Clean up after the execution context, which is only usable once.
+ if (m_capturedContext != null) m_capturedContext.Dispose();
+ }
+ }
+
+ /// <summary>Invokes or schedules the action to be executed.</summary>
+ /// <param name="action">The action to invoke or queue.</param>
+ /// <param name="allowInlining">
+ /// true to allow inlining, or false to force the action to run asynchronously.
+ /// </param>
+ /// <param name="currentTask">
+ /// A reference to the t_currentTask thread static value.
+ /// This is passed by-ref rather than accessed in the method in order to avoid
+ /// unnecessary thread-static writes.
+ /// </param>
+ /// <remarks>
+ /// No ExecutionContext work is performed used. This method is only used in the
+ /// case where a raw Action continuation delegate was stored into the Task, which
+ /// only happens in Task.SetContinuationForAwait if execution context flow was disabled
+ /// via using TaskAwaiter.UnsafeOnCompleted or a similar path.
+ /// </remarks>
+ [SecurityCritical]
+ internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
+ {
+ Contract.Assert(currentTask == Task.t_currentTask);
+
+ // If we're not allowed to run here, schedule the action
+ if (!allowInlining || !IsValidLocationForInlining)
+ {
+ UnsafeScheduleAction(action, currentTask);
+ return;
+ }
+
+ // Otherwise, run it, making sure that t_currentTask is null'd out appropriately during the execution
+ Task prevCurrentTask = currentTask;
+ try
+ {
+ if (prevCurrentTask != null) currentTask = null;
+ action();
+ }
+ catch (Exception exception)
+ {
+ ThrowAsyncIfNecessary(exception);
+ }
+ finally
+ {
+ if (prevCurrentTask != null) currentTask = prevCurrentTask;
+ }
+ }
+
+ /// <summary>Schedules the action to be executed. No ExecutionContext work is performed used.</summary>
+ /// <param name="action">The action to invoke or queue.</param>
+ [SecurityCritical]
+ internal static void UnsafeScheduleAction(Action action, Task task)
+ {
+ AwaitTaskContinuation atc = new AwaitTaskContinuation(action, flowExecutionContext: false);
+
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.IsEnabled() && task != null)
+ {
+ atc.m_continuationId = Task.NewId();
+ etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, atc.m_continuationId);
+ }
+
+ ThreadPool.UnsafeQueueCustomWorkItem(atc, forceGlobal: false);
+ }
+
+ /// <summary>Throws the exception asynchronously on the ThreadPool.</summary>
+ /// <param name="exc">The exception to throw.</param>
+ protected static void ThrowAsyncIfNecessary(Exception exc)
+ {
+ // Awaits should never experience an exception (other than an TAE or ADUE),
+ // unless a malicious user is explicitly passing a throwing action into the TaskAwaiter.
+ // We don't want to allow the exception to propagate on this stack, as it'll emerge in random places,
+ // and we can't fail fast, as that would allow for elevation of privilege.
+ //
+ // If unhandled error reporting APIs are available use those, otherwise since this
+ // would have executed on the thread pool otherwise, let it propagate there.
+
+ if (!(exc is ThreadAbortException || exc is AppDomainUnloadedException))
+ {
+#if FEATURE_COMINTEROP
+ if (!WindowsRuntimeMarshal.ReportUnhandledError(exc))
+#endif // FEATURE_COMINTEROP
+ {
+ var edi = ExceptionDispatchInfo.Capture(exc);
+ ThreadPool.QueueUserWorkItem(s => ((ExceptionDispatchInfo)s).Throw(), edi);
+ }
+ }
+ }
+
+ internal override Delegate[] GetDelegateContinuationsForDebugger()
+ {
+ Contract.Assert(m_action != null);
+ return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) };
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs
new file mode 100644
index 0000000000..198db8e15c
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs
@@ -0,0 +1,424 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// An abstraction for holding and aggregating exceptions.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+// Disable the "reference to volatile field not treated as volatile" error.
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.ExceptionServices;
+ using System.Security;
+
+ /// <summary>
+ /// An exception holder manages a list of exceptions for one particular task.
+ /// It offers the ability to aggregate, but more importantly, also offers intrinsic
+ /// support for propagating unhandled exceptions that are never observed. It does
+ /// this by aggregating and throwing if the holder is ever GC'd without the holder's
+ /// contents ever having been requested (e.g. by a Task.Wait, Task.get_Exception, etc).
+ /// This behavior is prominent in .NET 4 but is suppressed by default beyond that release.
+ /// </summary>
+ internal class TaskExceptionHolder
+ {
+ /// <summary>Whether we should propagate exceptions on the finalizer.</summary>
+ private readonly static bool s_failFastOnUnobservedException = ShouldFailFastOnUnobservedException();
+ /// <summary>Whether the AppDomain has started to unload.</summary>
+ private static volatile bool s_domainUnloadStarted;
+ /// <summary>An event handler used to notify of domain unload.</summary>
+ private static volatile EventHandler s_adUnloadEventHandler;
+
+ /// <summary>The task with which this holder is associated.</summary>
+ private readonly Task m_task;
+ /// <summary>
+ /// The lazily-initialized list of faulting exceptions. Volatile
+ /// so that it may be read to determine whether any exceptions were stored.
+ /// </summary>
+ private volatile List<ExceptionDispatchInfo> m_faultExceptions;
+ /// <summary>An exception that triggered the task to cancel.</summary>
+ private ExceptionDispatchInfo m_cancellationException;
+ /// <summary>Whether the holder was "observed" and thus doesn't cause finalization behavior.</summary>
+ private volatile bool m_isHandled;
+
+ /// <summary>
+ /// Creates a new holder; it will be registered for finalization.
+ /// </summary>
+ /// <param name="task">The task this holder belongs to.</param>
+ internal TaskExceptionHolder(Task task)
+ {
+ Contract.Requires(task != null, "Expected a non-null task.");
+ m_task = task;
+ EnsureADUnloadCallbackRegistered();
+ }
+
+ [SecuritySafeCritical]
+ private static bool ShouldFailFastOnUnobservedException()
+ {
+ bool shouldFailFast = false;
+ #if !FEATURE_CORECLR
+ shouldFailFast = System.CLRConfig.CheckThrowUnobservedTaskExceptions();
+ #endif
+ return shouldFailFast;
+ }
+
+ private static void EnsureADUnloadCallbackRegistered()
+ {
+ if (s_adUnloadEventHandler == null &&
+ Interlocked.CompareExchange( ref s_adUnloadEventHandler,
+ AppDomainUnloadCallback,
+ null) == null)
+ {
+ AppDomain.CurrentDomain.DomainUnload += s_adUnloadEventHandler;
+ }
+ }
+
+ private static void AppDomainUnloadCallback(object sender, EventArgs e)
+ {
+ s_domainUnloadStarted = true;
+ }
+
+ /// <summary>
+ /// A finalizer that repropagates unhandled exceptions.
+ /// </summary>
+ ~TaskExceptionHolder()
+ {
+ // Raise unhandled exceptions only when we know that neither the process or nor the appdomain is being torn down.
+ // We need to do this filtering because all TaskExceptionHolders will be finalized during shutdown or unload
+ // regardles of reachability of the task (i.e. even if the user code was about to observe the task's exception),
+ // which can otherwise lead to spurious crashes during shutdown.
+ if (m_faultExceptions != null && !m_isHandled &&
+ !Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !s_domainUnloadStarted)
+ {
+ // We don't want to crash the finalizer thread if any ThreadAbortExceptions
+ // occur in the list or in any nested AggregateExceptions.
+ // (Don't rethrow ThreadAbortExceptions.)
+ foreach (ExceptionDispatchInfo edi in m_faultExceptions)
+ {
+ var exp = edi.SourceException;
+ AggregateException aggExp = exp as AggregateException;
+ if (aggExp != null)
+ {
+ AggregateException flattenedAggExp = aggExp.Flatten();
+ foreach (Exception innerExp in flattenedAggExp.InnerExceptions)
+ {
+ if (innerExp is ThreadAbortException)
+ return;
+ }
+ }
+ else if (exp is ThreadAbortException)
+ {
+ return;
+ }
+ }
+
+ // We will only propagate if this is truly unhandled. The reason this could
+ // ever occur is somewhat subtle: if a Task's exceptions are observed in some
+ // other finalizer, and the Task was finalized before the holder, the holder
+ // will have been marked as handled before even getting here.
+
+ // Give users a chance to keep this exception from crashing the process
+
+ // First, publish the unobserved exception and allow users to observe it
+ AggregateException exceptionToThrow = new AggregateException(
+ Environment.GetResourceString("TaskExceptionHolder_UnhandledException"),
+ m_faultExceptions);
+ UnobservedTaskExceptionEventArgs ueea = new UnobservedTaskExceptionEventArgs(exceptionToThrow);
+ TaskScheduler.PublishUnobservedTaskException(m_task, ueea);
+
+ // Now, if we are still unobserved and we're configured to crash on unobserved, throw the exception.
+ // We need to publish the event above even if we're not going to crash, hence
+ // why this check doesn't come at the beginning of the method.
+ if (s_failFastOnUnobservedException && !ueea.m_observed)
+ {
+ throw exceptionToThrow;
+ }
+ }
+ }
+
+ /// <summary>Gets whether the exception holder is currently storing any exceptions for faults.</summary>
+ internal bool ContainsFaultList { get { return m_faultExceptions != null; } }
+
+ /// <summary>
+ /// Add an exception to the holder. This will ensure the holder is
+ /// in the proper state (handled/unhandled) depending on the list's contents.
+ /// </summary>
+ /// <param name="exceptionObject">
+ /// An exception object (either an Exception, an ExceptionDispatchInfo,
+ /// an IEnumerable{Exception}, or an IEnumerable{ExceptionDispatchInfo})
+ /// to add to the list.
+ /// </param>
+ /// <remarks>
+ /// Must be called under lock.
+ /// </remarks>
+ internal void Add(object exceptionObject)
+ {
+ Add(exceptionObject, representsCancellation: false);
+ }
+
+ /// <summary>
+ /// Add an exception to the holder. This will ensure the holder is
+ /// in the proper state (handled/unhandled) depending on the list's contents.
+ /// </summary>
+ /// <param name="representsCancellation">
+ /// Whether the exception represents a cancellation request (true) or a fault (false).
+ /// </param>
+ /// <param name="exceptionObject">
+ /// An exception object (either an Exception, an ExceptionDispatchInfo,
+ /// an IEnumerable{Exception}, or an IEnumerable{ExceptionDispatchInfo})
+ /// to add to the list.
+ /// </param>
+ /// <remarks>
+ /// Must be called under lock.
+ /// </remarks>
+ internal void Add(object exceptionObject, bool representsCancellation)
+ {
+ Contract.Requires(exceptionObject != null, "TaskExceptionHolder.Add(): Expected a non-null exceptionObject");
+ Contract.Requires(
+ exceptionObject is Exception || exceptionObject is IEnumerable<Exception> ||
+ exceptionObject is ExceptionDispatchInfo || exceptionObject is IEnumerable<ExceptionDispatchInfo>,
+ "TaskExceptionHolder.Add(): Expected Exception, IEnumerable<Exception>, ExceptionDispatchInfo, or IEnumerable<ExceptionDispatchInfo>");
+
+ if (representsCancellation) SetCancellationException(exceptionObject);
+ else AddFaultException(exceptionObject);
+ }
+
+ /// <summary>Sets the cancellation exception.</summary>
+ /// <param name="exceptionObject">The cancellation exception.</param>
+ /// <remarks>
+ /// Must be called under lock.
+ /// </remarks>
+ private void SetCancellationException(object exceptionObject)
+ {
+ Contract.Requires(exceptionObject != null, "Expected exceptionObject to be non-null.");
+
+ Contract.Assert(m_cancellationException == null,
+ "Expected SetCancellationException to be called only once.");
+ // Breaking this assumption will overwrite a previously OCE,
+ // and implies something may be wrong elsewhere, since there should only ever be one.
+
+ Contract.Assert(m_faultExceptions == null,
+ "Expected SetCancellationException to be called before any faults were added.");
+ // Breaking this assumption shouldn't hurt anything here, but it implies something may be wrong elsewhere.
+ // If this changes, make sure to only conditionally mark as handled below.
+
+ // Store the cancellation exception
+ var oce = exceptionObject as OperationCanceledException;
+ if (oce != null)
+ {
+ m_cancellationException = ExceptionDispatchInfo.Capture(oce);
+ }
+ else
+ {
+ var edi = exceptionObject as ExceptionDispatchInfo;
+ Contract.Assert(edi != null && edi.SourceException is OperationCanceledException,
+ "Expected an OCE or an EDI that contained an OCE");
+ m_cancellationException = edi;
+ }
+
+ // This is just cancellation, and there are no faults, so mark the holder as handled.
+ MarkAsHandled(false);
+ }
+
+ /// <summary>Adds the exception to the fault list.</summary>
+ /// <param name="exceptionObject">The exception to store.</param>
+ /// <remarks>
+ /// Must be called under lock.
+ /// </remarks>
+ private void AddFaultException(object exceptionObject)
+ {
+ Contract.Requires(exceptionObject != null, "AddFaultException(): Expected a non-null exceptionObject");
+
+ // Initialize the exceptions list if necessary. The list should be non-null iff it contains exceptions.
+ var exceptions = m_faultExceptions;
+ if (exceptions == null) m_faultExceptions = exceptions = new List<ExceptionDispatchInfo>(1);
+ else Contract.Assert(exceptions.Count > 0, "Expected existing exceptions list to have > 0 exceptions.");
+
+ // Handle Exception by capturing it into an ExceptionDispatchInfo and storing that
+ var exception = exceptionObject as Exception;
+ if (exception != null)
+ {
+ exceptions.Add(ExceptionDispatchInfo.Capture(exception));
+ }
+ else
+ {
+ // Handle ExceptionDispatchInfo by storing it into the list
+ var edi = exceptionObject as ExceptionDispatchInfo;
+ if (edi != null)
+ {
+ exceptions.Add(edi);
+ }
+ else
+ {
+ // Handle enumerables of exceptions by capturing each of the contained exceptions into an EDI and storing it
+ var exColl = exceptionObject as IEnumerable<Exception>;
+ if (exColl != null)
+ {
+#if DEBUG
+ int numExceptions = 0;
+#endif
+ foreach (var exc in exColl)
+ {
+#if DEBUG
+ Contract.Assert(exc != null, "No exceptions should be null");
+ numExceptions++;
+#endif
+ exceptions.Add(ExceptionDispatchInfo.Capture(exc));
+ }
+#if DEBUG
+ Contract.Assert(numExceptions > 0, "Collection should contain at least one exception.");
+#endif
+ }
+ else
+ {
+ // Handle enumerables of EDIs by storing them directly
+ var ediColl = exceptionObject as IEnumerable<ExceptionDispatchInfo>;
+ if (ediColl != null)
+ {
+ exceptions.AddRange(ediColl);
+#if DEBUG
+ Contract.Assert(exceptions.Count > 0, "There should be at least one dispatch info.");
+ foreach(var tmp in exceptions)
+ {
+ Contract.Assert(tmp != null, "No dispatch infos should be null");
+ }
+#endif
+ }
+ // Anything else is a programming error
+ else
+ {
+ throw new ArgumentException(Environment.GetResourceString("TaskExceptionHolder_UnknownExceptionType"), "exceptionObject");
+ }
+ }
+ }
+ }
+
+
+ // If all of the exceptions are ThreadAbortExceptions and/or
+ // AppDomainUnloadExceptions, we do not want the finalization
+ // probe to propagate them, so we consider the holder to be
+ // handled. If a subsequent exception comes in of a different
+ // kind, we will reactivate the holder.
+ for (int i = 0; i < exceptions.Count; i++)
+ {
+ var t = exceptions[i].SourceException.GetType();
+ if (t != typeof(ThreadAbortException) && t != typeof(AppDomainUnloadedException))
+ {
+ MarkAsUnhandled();
+ break;
+ }
+ else if (i == exceptions.Count - 1)
+ {
+ MarkAsHandled(false);
+ }
+ }
+ }
+
+ /// <summary>
+ /// A private helper method that ensures the holder is considered
+ /// unhandled, i.e. it is registered for finalization.
+ /// </summary>
+ private void MarkAsUnhandled()
+ {
+ // If a thread partially observed this thread's exceptions, we
+ // should revert back to "not handled" so that subsequent exceptions
+ // must also be seen. Otherwise, some could go missing. We also need
+ // to reregister for finalization.
+ if (m_isHandled)
+ {
+ GC.ReRegisterForFinalize(this);
+ m_isHandled = false;
+ }
+ }
+
+ /// <summary>
+ /// A private helper method that ensures the holder is considered
+ /// handled, i.e. it is not registered for finalization.
+ /// </summary>
+ /// <param name="calledFromFinalizer">Whether this is called from the finalizer thread.</param>
+ internal void MarkAsHandled(bool calledFromFinalizer)
+ {
+ if (!m_isHandled)
+ {
+ if (!calledFromFinalizer)
+ {
+ GC.SuppressFinalize(this);
+ }
+
+ m_isHandled = true;
+ }
+ }
+
+ /// <summary>
+ /// Allocates a new aggregate exception and adds the contents of the list to
+ /// it. By calling this method, the holder assumes exceptions to have been
+ /// "observed", such that the finalization check will be subsequently skipped.
+ /// </summary>
+ /// <param name="calledFromFinalizer">Whether this is being called from a finalizer.</param>
+ /// <param name="includeThisException">An extra exception to be included (optionally).</param>
+ /// <returns>The aggregate exception to throw.</returns>
+ internal AggregateException CreateExceptionObject(bool calledFromFinalizer, Exception includeThisException)
+ {
+ var exceptions = m_faultExceptions;
+ Contract.Assert(exceptions != null, "Expected an initialized list.");
+ Contract.Assert(exceptions.Count > 0, "Expected at least one exception.");
+
+ // Mark as handled and aggregate the exceptions.
+ MarkAsHandled(calledFromFinalizer);
+
+ // If we're only including the previously captured exceptions,
+ // return them immediately in an aggregate.
+ if (includeThisException == null)
+ return new AggregateException(exceptions);
+
+ // Otherwise, the caller wants a specific exception to be included,
+ // so return an aggregate containing that exception and the rest.
+ Exception[] combinedExceptions = new Exception[exceptions.Count + 1];
+ for (int i = 0; i < combinedExceptions.Length - 1; i++)
+ {
+ combinedExceptions[i] = exceptions[i].SourceException;
+ }
+ combinedExceptions[combinedExceptions.Length - 1] = includeThisException;
+ return new AggregateException(combinedExceptions);
+ }
+
+ /// <summary>
+ /// Wraps the exception dispatch infos into a new read-only collection. By calling this method,
+ /// the holder assumes exceptions to have been "observed", such that the finalization
+ /// check will be subsequently skipped.
+ /// </summary>
+ internal ReadOnlyCollection<ExceptionDispatchInfo> GetExceptionDispatchInfos()
+ {
+ var exceptions = m_faultExceptions;
+ Contract.Assert(exceptions != null, "Expected an initialized list.");
+ Contract.Assert(exceptions.Count > 0, "Expected at least one exception.");
+ MarkAsHandled(false);
+ return new ReadOnlyCollection<ExceptionDispatchInfo>(exceptions);
+ }
+
+ /// <summary>
+ /// Gets the ExceptionDispatchInfo representing the singular exception
+ /// that was the cause of the task's cancellation.
+ /// </summary>
+ /// <returns>
+ /// The ExceptionDispatchInfo for the cancellation exception. May be null.
+ /// </returns>
+ internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo()
+ {
+ var edi = m_cancellationException;
+ Contract.Assert(edi == null || edi.SourceException is OperationCanceledException,
+ "Expected the EDI to be for an OperationCanceledException");
+ return edi;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs
new file mode 100644
index 0000000000..52b471628a
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs
@@ -0,0 +1,3206 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// There are a plethora of common patterns for which Tasks are created. TaskFactory encodes
+// these patterns into helper methods. These helpers also pick up default configuration settings
+// applicable to the entire factory and configurable through its constructors.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides support for creating and scheduling
+ /// <see cref="T:System.Threading.Tasks.Task">Tasks</see>.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// There are many common patterns for which tasks are relevant. The <see cref="TaskFactory"/>
+ /// class encodes some of these patterns into methods that pick up default settings, which are
+ /// configurable through its constructors.
+ /// </para>
+ /// <para>
+ /// A default instance of <see cref="TaskFactory"/> is available through the
+ /// <see cref="System.Threading.Tasks.Task.Factory">Task.Factory</see> property.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class TaskFactory
+ {
+ // member variables
+ private CancellationToken m_defaultCancellationToken;
+ private TaskScheduler m_defaultScheduler;
+ private TaskCreationOptions m_defaultCreationOptions;
+ private TaskContinuationOptions m_defaultContinuationOptions;
+
+
+ private TaskScheduler DefaultScheduler
+ {
+ get
+ {
+ if (m_defaultScheduler == null) return TaskScheduler.Current;
+ else return m_defaultScheduler;
+ }
+ }
+
+ // sister method to above property -- avoids a TLS lookup
+ private TaskScheduler GetDefaultScheduler(Task currTask)
+ {
+ if (m_defaultScheduler != null) return m_defaultScheduler;
+ else if ((currTask != null)
+ && ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
+ )
+ return currTask.ExecutingTaskScheduler;
+ else return TaskScheduler.Default;
+ }
+
+ /* Constructors */
+
+ // ctor parameters provide defaults for the factory, which can be overridden by options provided to
+ // specific calls on the factory
+
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory"/> instance with the default configuration.
+ /// </summary>
+ /// <remarks>
+ /// This constructor creates a <see cref="TaskFactory"/> instance with a default configuration. The
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory()
+ : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned
+ /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified
+ /// while calling the factory methods.</param>
+ /// <remarks>
+ /// This constructor creates a <see cref="TaskFactory"/> instance with a default configuration. The
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(CancellationToken cancellationToken)
+ : this(cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="scheduler">
+ /// The <see cref="System.Threading.Tasks.TaskScheduler">
+ /// TaskScheduler</see> to use to schedule any tasks created with this TaskFactory. A null value
+ /// indicates that the current TaskScheduler should be used.
+ /// </param>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to
+ /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>,
+ /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is
+ /// initialized to <paramref name="scheduler"/>, unless it's null, in which case the property is
+ /// initialized to the current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(TaskScheduler scheduler) // null means to use TaskScheduler.Current
+ : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, scheduler)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="creationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskCreationOptions">
+ /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory.
+ /// </param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/>
+ /// argument specifies an invalid value.
+ /// </exception>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>,
+ /// the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref
+ /// name="continuationOptions"/>, and the <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to the
+ /// current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions)
+ : this(default(CancellationToken), creationOptions, continuationOptions, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="TaskFactory"/> instance with the specified configuration.
+ /// </summary>
+ /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned
+ /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified
+ /// while calling the factory methods.</param>
+ /// <param name="creationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskCreationOptions">
+ /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory.
+ /// </param>
+ /// <param name="scheduler">
+ /// The default <see cref="System.Threading.Tasks.TaskScheduler">
+ /// TaskScheduler</see> to use to schedule any Tasks created with this TaskFactory. A null value
+ /// indicates that TaskScheduler.Current should be used.
+ /// </param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/>
+ /// argumentspecifies an invalid value.
+ /// </exception>
+ /// <remarks>
+ /// With this constructor, the
+ /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>,
+ /// the
+ /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref
+ /// name="continuationOptions"/>, and the <see
+ /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to
+ /// <paramref name="scheduler"/>, unless it's null, in which case the property is initialized to the
+ /// current scheduler (see <see
+ /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>).
+ /// </remarks>
+ public TaskFactory(CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ CheckMultiTaskContinuationOptions(continuationOptions);
+ CheckCreationOptions(creationOptions);
+
+ m_defaultCancellationToken = cancellationToken;
+ m_defaultScheduler = scheduler;
+ m_defaultCreationOptions = creationOptions;
+ m_defaultContinuationOptions = continuationOptions;
+ }
+
+ [ContractArgumentValidatorAttribute]
+ internal static void CheckCreationOptions(TaskCreationOptions creationOptions)
+ {
+ // Check for validity of options
+ if ((creationOptions &
+ ~(TaskCreationOptions.AttachedToParent |
+ TaskCreationOptions.DenyChildAttach |
+ TaskCreationOptions.HideScheduler |
+ TaskCreationOptions.LongRunning |
+ TaskCreationOptions.PreferFairness |
+ TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
+ {
+ throw new ArgumentOutOfRangeException("creationOptions");
+ }
+ Contract.EndContractBlock();
+ }
+
+ /* Properties */
+
+ /// <summary>
+ /// Gets the default <see cref="System.Threading.CancellationToken">CancellationToken</see> of this
+ /// TaskFactory.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default <see cref="CancellationToken"/> that will be assigned to all
+ /// tasks created by this factory unless another CancellationToken value is explicitly specified
+ /// during the call to the factory methods.
+ /// </remarks>
+ public CancellationToken CancellationToken { get { return m_defaultCancellationToken; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> of this
+ /// TaskFactory.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default scheduler for this factory. It will be used to schedule all
+ /// tasks unless another scheduler is explicitly specified during calls to this factory's methods.
+ /// If null, <see cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>
+ /// will be used.
+ /// </remarks>
+ public TaskScheduler Scheduler { get { return m_defaultScheduler; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions
+ /// </see> value of this TaskFactory.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default creation options for this factory. They will be used to create all
+ /// tasks unless other options are explicitly specified during calls to this factory's methods.
+ /// </remarks>
+ public TaskCreationOptions CreationOptions { get { return m_defaultCreationOptions; } }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskContinuationOptions
+ /// </see> value of this TaskFactory.
+ /// </summary>
+ /// <remarks>
+ /// This property returns the default continuation options for this factory. They will be used to create
+ /// all continuation tasks unless other options are explicitly specified during calls to this factory's methods.
+ /// </remarks>
+ public TaskContinuationOptions ContinuationOptions { get { return m_defaultContinuationOptions; } }
+
+ //
+ // StartNew methods
+ //
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="action"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors
+ /// and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. However,
+ /// unless creation and scheduling must be separated, StartNew is the recommended
+ /// approach for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action action)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, null, m_defaultCancellationToken, GetDefaultScheduler(currTask),
+ m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors
+ /// and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. However,
+ /// unless creation and scheduling must be separated, StartNew is the recommended
+ /// approach for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action action, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, null, cancellationToken, GetDefaultScheduler(currTask),
+ m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task">Task.</see></param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action action, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, null, m_defaultCancellationToken, GetDefaultScheduler(currTask), creationOptions,
+ InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new <see cref="Task"/></param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task">Task.</see></param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task.InternalStartNew(
+ Task.InternalCurrentIfAttached(creationOptions), action, null, cancellationToken, scheduler, creationOptions,
+ InternalTaskOptions.None, ref stackMark);
+ }
+
+ // Internal version includes InternalTaskOptions for Parallel.Invoke() support.
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ internal Task StartNew(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task.InternalStartNew(
+ Task.InternalCurrentIfAttached(creationOptions), action, null, cancellationToken, scheduler, creationOptions, internalOptions, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="action"/>
+ /// delegate.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action<Object> action, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, state, m_defaultCancellationToken, GetDefaultScheduler(currTask),
+ m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="action"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new <see cref="Task"/></param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action<Object> action, Object state, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, state, cancellationToken, GetDefaultScheduler(currTask),
+ m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="action"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task">Task.</see></param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action<Object> action, Object state, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task.InternalStartNew(currTask, action, state, m_defaultCancellationToken, GetDefaultScheduler(currTask),
+ creationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task">Task</see>.
+ /// </summary>
+ /// <param name="action">The action delegate to execute asynchronously.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="action"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task">Task.</see></param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="action"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a Task using one of its constructors and
+ /// then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task StartNew(Action<Object> action, Object state, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task.InternalStartNew(
+ Task.InternalCurrentIfAttached(creationOptions), action, state, cancellationToken, scheduler,
+ creationOptions, InternalTaskOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<TResult> function)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new <see cref="Task"/></param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<TResult> function, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, cancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<TResult> function, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken,
+ creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}">
+ /// Task{TResult}</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<TResult> function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(
+ Task.InternalCurrentIfAttached(creationOptions), function, cancellationToken,
+ creationOptions, InternalTaskOptions.None, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<Object, TResult> function, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new <see cref="Task"/></param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<Object, TResult> function, Object state, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, cancellationToken,
+ m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<Object, TResult> function, Object state, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Task currTask = Task.InternalCurrent;
+ return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken,
+ creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="function">A function delegate that returns the future result to be available through
+ /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="function"/>
+ /// delegate.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the
+ /// created
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <param name="scheduler">The <see
+ /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}">
+ /// Task{TResult}</see>.</param>
+ /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="function"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref
+ /// name="scheduler"/>
+ /// argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one
+ /// of its constructors and then calling
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution.
+ /// However, unless creation and scheduling must be separated, StartNew is the recommended approach
+ /// for both simplicity and performance.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> StartNew<TResult>(Func<Object, TResult> function, Object state, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Task<TResult>.StartNew(
+ Task.InternalCurrentIfAttached(creationOptions), function, state, cancellationToken,
+ creationOptions, InternalTaskOptions.None, scheduler, ref stackMark);
+ }
+
+ //
+ // FromAsync methods
+ //
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that executes an end method action
+ /// when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The action delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the asynchronous
+ /// operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task FromAsync(
+ IAsyncResult asyncResult,
+ Action<IAsyncResult> endMethod)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsync(asyncResult, endMethod, m_defaultCreationOptions, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that executes an end method action
+ /// when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The action delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the asynchronous
+ /// operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task FromAsync(
+ IAsyncResult asyncResult,
+ Action<IAsyncResult> endMethod,
+ TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsync(asyncResult, endMethod, creationOptions, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that executes an end method action
+ /// when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The action delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the task that executes the end method.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the asynchronous
+ /// operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task FromAsync(
+ IAsyncResult asyncResult,
+ Action<IAsyncResult> endMethod,
+ TaskCreationOptions creationOptions,
+ TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return FromAsync(asyncResult, endMethod, creationOptions, scheduler, ref stackMark);
+ }
+
+ // private version that supports StackCrawlMark.
+ private Task FromAsync(
+ IAsyncResult asyncResult,
+ Action<IAsyncResult> endMethod,
+ TaskCreationOptions creationOptions,
+ TaskScheduler scheduler,
+ ref StackCrawlMark stackMark)
+ {
+ return TaskFactory<VoidTaskResult>.FromAsyncImpl(asyncResult, null, endMethod, creationOptions, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ object state)
+ {
+ return FromAsync(beginMethod, endMethod, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<VoidTaskResult>.FromAsyncImpl(beginMethod, null, endMethod, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1>(
+ Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1,
+ object state)
+ {
+ return FromAsync(beginMethod, endMethod, arg1, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1>(
+ Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<VoidTaskResult>.FromAsyncImpl(beginMethod, null, endMethod, arg1, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1, TArg2>(
+ Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1, TArg2 arg2, object state)
+ {
+ return FromAsync(beginMethod, endMethod, arg1, arg2, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1, TArg2>(
+ Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<VoidTaskResult>.FromAsyncImpl(beginMethod, null, endMethod, arg1, arg2, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1, TArg2, TArg3>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1, TArg2 arg2, TArg3 arg3, object state)
+ {
+ return FromAsync(beginMethod, endMethod, arg1, arg2, arg3, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents a pair of begin
+ /// and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task FromAsync<TArg1, TArg2, TArg3>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Action<IAsyncResult> endMethod,
+ TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<VoidTaskResult>.FromAsyncImpl<TArg1, TArg2, TArg3>(beginMethod, null, endMethod, arg1, arg2, arg3, state, creationOptions);
+ }
+
+ //
+ // Additional FromAsync() overloads used for inferencing convenience
+ //
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync<TResult>(
+ IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync<TResult>(
+ IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod, TaskCreationOptions creationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end
+ /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the
+ /// <paramref name="endMethod"/>.</param>
+ /// <param name="endMethod">The function delegate that processes the completed <paramref
+ /// name="asyncResult"/>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the task that executes the end method.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="asyncResult"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the
+ /// asynchronous operation.</returns>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> FromAsync<TResult>(
+ IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod, TaskCreationOptions creationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TResult>(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, object state)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TResult>(
+ Func<AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TResult>(
+ Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, object state)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TResult>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TResult>(Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, TArg2 arg2, object state)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TResult>(
+ Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TArg3, TResult>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, TArg2 arg2, TArg3 arg3, object state)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, m_defaultCreationOptions);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of
+ /// begin and end methods that conform to the Asynchronous Programming Model pattern.
+ /// </summary>
+ /// <typeparam name="TArg1">The type of the first argument passed to the <paramref
+ /// name="beginMethod"/> delegate.</typeparam>
+ /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/>
+ /// delegate.</typeparam>
+ /// <typeparam name="TResult">The type of the result available through the
+ /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.
+ /// </typeparam>
+ /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param>
+ /// <param name="endMethod">The delegate that ends the asynchronous operation.</param>
+ /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
+ /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/>
+ /// delegate.</param>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="beginMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="endMethod"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions
+ /// value.</exception>
+ /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that
+ /// represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>.
+ /// </remarks>
+ public Task<TResult> FromAsync<TArg1, TArg2, TArg3, TResult>(
+ Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod,
+ Func<IAsyncResult, TResult> endMethod, TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
+ {
+ return TaskFactory<TResult>.FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, creationOptions);
+ }
+
+ /// <summary>
+ /// Check validity of options passed to FromAsync method
+ /// </summary>
+ /// <param name="creationOptions">The options to be validated.</param>
+ /// <param name="hasBeginMethod">determines type of FromAsync method that called this method</param>
+ internal static void CheckFromAsyncOptions(TaskCreationOptions creationOptions, bool hasBeginMethod)
+ {
+ if (hasBeginMethod)
+ {
+ // Options detected here cause exceptions in FromAsync methods that take beginMethod as a parameter
+ if ((creationOptions & TaskCreationOptions.LongRunning) != 0)
+ throw new ArgumentOutOfRangeException("creationOptions", Environment.GetResourceString("Task_FromAsync_LongRunning"));
+ if ((creationOptions & TaskCreationOptions.PreferFairness) != 0)
+ throw new ArgumentOutOfRangeException("creationOptions", Environment.GetResourceString("Task_FromAsync_PreferFairness"));
+ }
+
+ // Check for general validity of options
+ if ((creationOptions &
+ ~(TaskCreationOptions.AttachedToParent |
+ TaskCreationOptions.DenyChildAttach |
+ TaskCreationOptions.HideScheduler |
+ TaskCreationOptions.PreferFairness |
+ TaskCreationOptions.LongRunning)) != 0)
+ {
+ throw new ArgumentOutOfRangeException("creationOptions");
+ }
+ }
+
+
+ //
+ // ContinueWhenAll methods
+ //
+
+ // A Task<Task[]> that, given an initial collection of N tasks, will complete when
+ // it has been invoked N times. This allows us to replace this logic:
+ // Task<Task[]> promise = new Task<Task[]>(...);
+ // int _count = tasksCopy.Length;
+ // Action<Task> completionAction = delegate {if(Interlocked.Decrement(ref _count) == 0) promise.TrySetResult(tasksCopy);
+ // for(int i=0; i<_count; i++)
+ // tasksCopy[i].AddCompletionAction(completionAction);
+ // with this logic:
+ // CompletionOnCountdownPromise promise = new CompletionOnCountdownPromise(tasksCopy);
+ // for(int i=0; i<tasksCopy.Length; i++) tasksCopy[i].AddCompletionAction(promise);
+ // which saves a few allocations.
+ //
+ // Used in TaskFactory.CommonCWAllLogic(Task[]), below.
+ private sealed class CompleteOnCountdownPromise : Task<Task[]>, ITaskCompletionAction
+ {
+ private readonly Task[] _tasks;
+ private int _count;
+
+ internal CompleteOnCountdownPromise(Task[] tasksCopy) : base()
+ {
+ Contract.Requires((tasksCopy != null) && (tasksCopy.Length > 0), "Expected non-null task array with at least one element in it");
+ _tasks = tasksCopy;
+ _count = tasksCopy.Length;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAll", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+ }
+
+ public void Invoke(Task completingTask)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
+
+ if (completingTask.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
+ if (Interlocked.Decrement(ref _count) == 0)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+
+ TrySetResult(_tasks);
+ }
+ Contract.Assert(_count >= 0, "Count should never go below 0");
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+
+ /// <summary>
+ /// Returns whether we should notify the debugger of a wait completion. This returns
+ /// true iff at least one constituent task has its bit set.
+ /// </summary>
+ internal override bool ShouldNotifyDebuggerOfWaitCompletion
+ {
+ get
+ {
+ return
+ base.ShouldNotifyDebuggerOfWaitCompletion &&
+ Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(_tasks);
+ }
+ }
+ }
+
+ // Performs some logic common to all ContinueWhenAll() overloads
+ internal static Task<Task[]> CommonCWAllLogic(Task[] tasksCopy)
+ {
+ Contract.Requires(tasksCopy != null);
+
+ // Create a promise task to be returned to the user
+ CompleteOnCountdownPromise promise = new CompleteOnCountdownPromise(tasksCopy);
+
+ for (int i = 0; i < tasksCopy.Length; i++)
+ {
+ if (tasksCopy[i].IsCompleted) promise.Invoke(tasksCopy[i]); // Short-circuit the completion action, if possible
+ else tasksCopy[i].AddCompletionAction(promise); // simple completion action
+ }
+
+ return promise;
+ }
+
+
+ // A Task<Task<T>[]> that, given an initial collection of N tasks, will complete when
+ // it has been invoked N times. See comments for non-generic CompleteOnCountdownPromise class.
+ //
+ // Used in TaskFactory.CommonCWAllLogic<TResult>(Task<TResult>[]), below.
+ private sealed class CompleteOnCountdownPromise<T> : Task<Task<T>[]>, ITaskCompletionAction
+ {
+ private readonly Task<T>[] _tasks;
+ private int _count;
+
+ internal CompleteOnCountdownPromise(Task<T>[] tasksCopy) : base()
+ {
+ Contract.Requires((tasksCopy != null) && (tasksCopy.Length > 0), "Expected non-null task array with at least one element in it");
+ _tasks = tasksCopy;
+ _count = tasksCopy.Length;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAll<>", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+ }
+
+ public void Invoke(Task completingTask)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
+
+ if (completingTask.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
+ if (Interlocked.Decrement(ref _count) == 0)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+
+ TrySetResult(_tasks);
+ }
+ Contract.Assert(_count >= 0, "Count should never go below 0");
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+
+ /// <summary>
+ /// Returns whether we should notify the debugger of a wait completion. This returns
+ /// true iff at least one constituent task has its bit set.
+ /// </summary>
+ internal override bool ShouldNotifyDebuggerOfWaitCompletion
+ {
+ get
+ {
+ return
+ base.ShouldNotifyDebuggerOfWaitCompletion &&
+ Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(_tasks);
+ }
+ }
+ }
+
+
+ internal static Task<Task<T>[]> CommonCWAllLogic<T>(Task<T>[] tasksCopy)
+ {
+ Contract.Requires(tasksCopy != null);
+
+ // Create a promise task to be returned to the user
+ CompleteOnCountdownPromise<T> promise = new CompleteOnCountdownPromise<T>(tasksCopy);
+
+ for (int i = 0; i < tasksCopy.Length; i++)
+ {
+ if (tasksCopy[i].IsCompleted) promise.Invoke(tasksCopy[i]); // Short-circuit the completion action, if possible
+ else tasksCopy[i].AddCompletionAction(promise); // simple completion action
+ }
+
+ return promise;
+ }
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll(Task[] tasks, Action<Task[]> continuationAction)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll(Task[] tasks, Action<Task[]> continuationAction, CancellationToken cancellationToken)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll(Task[] tasks, Action<Task[]> continuationAction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll(Task[] tasks, Action<Task[]> continuationAction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>[]> continuationAction)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in
+ /// the <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>[]> continuationAction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>[]> continuationAction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationAction">The action delegate to execute when all tasks in the <paramref
+ /// name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>[]> continuationAction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TResult>(Task[] tasks, Func<Task[], TResult> continuationFunction)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TResult>(Task[] tasks, Func<Task[], TResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TResult>(Task[] tasks, Func<Task[], TResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TResult>(Task[] tasks, Func<Task[], TResult> continuationFunction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of a set of provided Tasks.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue.</param>
+ /// <param name="continuationFunction">The function delegate to execute when all tasks in the
+ /// <paramref name="tasks"/> array have completed.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAll.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ //
+ // ContinueWhenAny methods
+ //
+
+ // A Task<Task> that will be completed the first time that Invoke is called.
+ // It allows us to replace this logic:
+ // Task<Task> promise = new Task<Task>(...);
+ // Action<Task> completionAction = delegate(Task completingTask) { promise.TrySetResult(completingTask); }
+ // for(int i=0; i<tasksCopy.Length; i++) tasksCopy[i].AddCompletionAction(completionAction);
+ // with this logic:
+ // CompletionOnInvokePromise promise = new CompletionOnInvokePromise(tasksCopy);
+ // for(int i=0; i<tasksCopy.Length; i++) tasksCopy[i].AddCompletionAction(promise);
+ // which saves a couple of allocations.
+ //
+ // Used in TaskFactory.CommonCWAnyLogic(), below.
+ internal sealed class CompleteOnInvokePromise : Task<Task>, ITaskCompletionAction
+ {
+ private IList<Task> _tasks; // must track this for cleanup
+ private int m_firstTaskAlreadyCompleted;
+
+ public CompleteOnInvokePromise(IList<Task> tasks) : base()
+ {
+ Contract.Requires(tasks != null, "Expected non-null collection of tasks");
+ _tasks = tasks;
+
+ if (AsyncCausalityTracer.LoggingOn)
+ AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAny", 0);
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ AddToActiveTasks(this);
+ }
+ }
+
+ public void Invoke(Task completingTask)
+ {
+ if (Interlocked.CompareExchange(ref m_firstTaskAlreadyCompleted, 1, 0) == 0)
+ {
+ if (AsyncCausalityTracer.LoggingOn)
+ {
+ AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Choice);
+ AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
+ }
+
+ if (Task.s_asyncDebuggingEnabled)
+ {
+ RemoveFromActiveTasks(this.Id);
+ }
+
+ bool success = TrySetResult(completingTask);
+ Contract.Assert(success, "Only one task should have gotten to this point, and thus this must be successful.");
+
+ // We need to remove continuations that may be left straggling on other tasks.
+ // Otherwise, repeated calls to WhenAny using the same task could leak actions.
+ // This may also help to avoided unnecessary invocations of this whenComplete delegate.
+ // Note that we may be attempting to remove a continuation from a task that hasn't had it
+ // added yet; while there's overhead there, the operation won't hurt anything.
+ var tasks = _tasks;
+ int numTasks = tasks.Count;
+ for (int i = 0; i < numTasks; i++)
+ {
+ var task = tasks[i];
+ if (task != null && // if an element was erroneously nulled out concurrently, just skip it; worst case is we don't remove a continuation
+ !task.IsCompleted) task.RemoveContinuation(this);
+ }
+ _tasks = null;
+
+ }
+ }
+
+ public bool InvokeMayRunArbitraryCode { get { return true; } }
+ }
+ // Common ContinueWhenAny logic
+ // If the tasks list is not an array, it must be an internal defensive copy so that
+ // we don't need to be concerned about concurrent modifications to the list. If the task list
+ // is an array, it should be a defensive copy if this functionality is being used
+ // asynchronously (e.g. WhenAny) rather than synchronously (e.g. WaitAny).
+ internal static Task<Task> CommonCWAnyLogic(IList<Task> tasks)
+ {
+ Contract.Requires(tasks != null);
+
+ // Create a promise task to be returned to the user
+ var promise = new CompleteOnInvokePromise(tasks);
+
+ // At the completion of any of the tasks, complete the promise.
+
+ bool checkArgsOnly = false;
+ int numTasks = tasks.Count;
+ for(int i=0; i<numTasks; i++)
+ {
+ var task = tasks[i];
+ if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
+
+ if (checkArgsOnly) continue;
+
+ // If the promise has already completed, don't bother with checking any more tasks.
+ if (promise.IsCompleted)
+ {
+ checkArgsOnly = true;
+ }
+ // If a task has already completed, complete the promise.
+ else if (task.IsCompleted)
+ {
+ promise.Invoke(task);
+ checkArgsOnly = true;
+ }
+ // Otherwise, add the completion action and keep going.
+ else task.AddCompletionAction(promise);
+ }
+
+ return promise;
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny(Task[] tasks, Action<Task> continuationAction)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny(Task[] tasks, Action<Task> continuationAction, CancellationToken cancellationToken)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny(Task[] tasks, Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the <paramref
+ /// name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny(Task[] tasks, Action<Task> continuationAction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TResult>(Task[] tasks, Func<Task, TResult> continuationFunction)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TResult>(Task[] tasks, Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TResult>(Task[] tasks, Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null,continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TResult>(Task[] tasks, Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result that is returned by the <paramref
+ /// name="continuationFunction"/>
+ /// delegate and associated with the created <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</typeparam>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationFunction">The function delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationFunction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TResult> ContinueWhenAny<TAntecedentResult, TResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>> continuationAction)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>> continuationAction,
+ CancellationToken cancellationToken)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>> continuationAction,
+ TaskContinuationOptions continuationOptions)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// that will be started upon the completion of any Task in the provided set.
+ /// </summary>
+ /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam>
+ /// <param name="tasks">The array of tasks from which to continue when one task completes.</param>
+ /// <param name="continuationAction">The action delegate to execute when one task in the
+ /// <paramref name="tasks"/> array completes.</param>
+ /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions">
+ /// TaskContinuationOptions</see> value that controls the behavior of
+ /// the created continuation <see cref="T:System.Threading.Tasks.Task">Task</see>.</param>
+ /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// that is used to schedule the created continuation <see
+ /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param>
+ /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="continuationAction"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the
+ /// <paramref name="scheduler"/> argument is null.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array contains a null value.</exception>
+ /// <exception cref="T:System.ArgumentException">The exception that is thrown when the
+ /// <paramref name="tasks"/> array is empty.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the
+ /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions
+ /// value.</exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ /// <remarks>
+ /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>,
+ /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation
+ /// will be executed, are illegal with ContinueWhenAny.
+ /// </remarks>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Action<Task<TAntecedentResult>> continuationAction,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ if (continuationAction == null) throw new ArgumentNullException("continuationAction");
+ Contract.EndContractBlock();
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark);
+ }
+
+ // Check task array and return a defensive copy.
+ // Used with ContinueWhenAll()/ContinueWhenAny().
+ internal static Task[] CheckMultiContinuationTasksAndCopy(Task[] tasks)
+ {
+ if (tasks == null)
+ throw new ArgumentNullException("tasks");
+ if (tasks.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
+ Contract.EndContractBlock();
+
+ Task[] tasksCopy = new Task[tasks.Length];
+ for (int i = 0; i < tasks.Length; i++)
+ {
+ tasksCopy[i] = tasks[i];
+
+ if (tasksCopy[i] == null)
+ throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
+ }
+
+ return tasksCopy;
+ }
+
+ internal static Task<TResult>[] CheckMultiContinuationTasksAndCopy<TResult>(Task<TResult>[] tasks)
+ {
+ if (tasks == null)
+ throw new ArgumentNullException("tasks");
+ if (tasks.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
+ Contract.EndContractBlock();
+
+ Task<TResult>[] tasksCopy = new Task<TResult>[tasks.Length];
+ for (int i = 0; i < tasks.Length; i++)
+ {
+ tasksCopy[i] = tasks[i];
+
+ if (tasksCopy[i] == null)
+ throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
+ }
+
+ return tasksCopy;
+ }
+
+ // Throw an exception if "options" argument specifies illegal options
+ [ContractArgumentValidatorAttribute]
+ internal static void CheckMultiTaskContinuationOptions(TaskContinuationOptions continuationOptions)
+ {
+ // Construct a mask to check for illegal options
+ const TaskContinuationOptions NotOnAny = TaskContinuationOptions.NotOnCanceled |
+ TaskContinuationOptions.NotOnFaulted |
+ TaskContinuationOptions.NotOnRanToCompletion;
+
+ // Check that LongRunning and ExecuteSynchronously are not specified together
+ const TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning;
+ if ((continuationOptions & illegalMask) == illegalMask)
+ {
+ throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_ContinueWith_ESandLR"));
+ }
+
+ // Check that no nonsensical options are specified.
+ if ((continuationOptions & ~(
+ TaskContinuationOptions.LongRunning |
+ TaskContinuationOptions.PreferFairness |
+ TaskContinuationOptions.AttachedToParent |
+ TaskContinuationOptions.DenyChildAttach |
+ TaskContinuationOptions.HideScheduler |
+ TaskContinuationOptions.LazyCancellation |
+ NotOnAny |
+ TaskContinuationOptions.ExecuteSynchronously)) != 0)
+ {
+ throw new ArgumentOutOfRangeException("continuationOptions");
+ }
+
+ // Check that no "fire" options are specified.
+ if ((continuationOptions & NotOnAny) != 0)
+ throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_MultiTaskContinuation_FireOptions"));
+ Contract.EndContractBlock();
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs
new file mode 100644
index 0000000000..f82492499c
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs
@@ -0,0 +1,757 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// This file contains the primary interface and management of tasks and queues.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+// Disable the "reference to volatile field not treated as volatile" error.
+#pragma warning disable 0420
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using System.Security;
+using System.Security.Permissions;
+using System.Collections.Concurrent;
+using System.Diagnostics.Contracts;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Threading.Tasks
+{
+
+ /// <summary>
+ /// Represents an abstract scheduler for tasks.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> acts as the extension point for all
+ /// pluggable scheduling logic. This includes mechanisms such as how to schedule a task for execution, and
+ /// how scheduled tasks should be exposed to debuggers.
+ /// </para>
+ /// <para>
+ /// All members of the abstract <see cref="TaskScheduler"/> type are thread-safe
+ /// and may be used from multiple threads concurrently.
+ /// </para>
+ /// </remarks>
+ [DebuggerDisplay("Id={Id}")]
+ [DebuggerTypeProxy(typeof(SystemThreadingTasks_TaskSchedulerDebugView))]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+#pragma warning disable 618
+ [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
+#pragma warning restore 618
+ public abstract class TaskScheduler
+ {
+ ////////////////////////////////////////////////////////////
+ //
+ // User Provided Methods and Properties
+ //
+
+ /// <summary>
+ /// Queues a <see cref="T:System.Threading.Tasks.Task">Task</see> to the scheduler.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A class derived from <see cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// implements this method to accept tasks being scheduled on the scheduler.
+ /// A typical implementation would store the task in an internal data structure, which would
+ /// be serviced by threads that would execute those tasks at some time in the future.
+ /// </para>
+ /// <para>
+ /// This method is only meant to be called by the .NET Framework and
+ /// should not be called directly by the derived class. This is necessary
+ /// for maintaining the consistency of the system.
+ /// </para>
+ /// </remarks>
+ /// <param name="task">The <see cref="T:System.Threading.Tasks.Task">Task</see> to be queued.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="task"/> argument is null.</exception>
+ [SecurityCritical]
+ protected internal abstract void QueueTask(Task task);
+
+ /// <summary>
+ /// Determines whether the provided <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// can be executed synchronously in this call, and if it can, executes it.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A class derived from <see cref="TaskScheduler">TaskScheduler</see> implements this function to
+ /// support inline execution of a task on a thread that initiates a wait on that task object. Inline
+ /// execution is optional, and the request may be rejected by returning false. However, better
+ /// scalability typically results the more tasks that can be inlined, and in fact a scheduler that
+ /// inlines too little may be prone to deadlocks. A proper implementation should ensure that a
+ /// request executing under the policies guaranteed by the scheduler can successfully inline. For
+ /// example, if a scheduler uses a dedicated thread to execute tasks, any inlining requests from that
+ /// thread should succeed.
+ /// </para>
+ /// <para>
+ /// If a scheduler decides to perform the inline execution, it should do so by calling to the base
+ /// TaskScheduler's
+ /// <see cref="TryExecuteTask">TryExecuteTask</see> method with the provided task object, propagating
+ /// the return value. It may also be appropriate for the scheduler to remove an inlined task from its
+ /// internal data structures if it decides to honor the inlining request. Note, however, that under
+ /// some circumstances a scheduler may be asked to inline a task that was not previously provided to
+ /// it with the <see cref="QueueTask"/> method.
+ /// </para>
+ /// <para>
+ /// The derived scheduler is responsible for making sure that the calling thread is suitable for
+ /// executing the given task as far as its own scheduling and execution policies are concerned.
+ /// </para>
+ /// </remarks>
+ /// <param name="task">The <see cref="T:System.Threading.Tasks.Task">Task</see> to be
+ /// executed.</param>
+ /// <param name="taskWasPreviouslyQueued">A Boolean denoting whether or not task has previously been
+ /// queued. If this parameter is True, then the task may have been previously queued (scheduled); if
+ /// False, then the task is known not to have been queued, and this call is being made in order to
+ /// execute the task inline without queueing it.</param>
+ /// <returns>A Boolean value indicating whether the task was executed inline.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="task"/> argument is
+ /// null.</exception>
+ /// <exception cref="T:System.InvalidOperationException">The <paramref name="task"/> was already
+ /// executed.</exception>
+ [SecurityCritical]
+ protected abstract bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued);
+
+ /// <summary>
+ /// Generates an enumerable of <see cref="T:System.Threading.Tasks.Task">Task</see> instances
+ /// currently queued to the scheduler waiting to be executed.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A class derived from <see cref="TaskScheduler"/> implements this method in order to support
+ /// integration with debuggers. This method will only be invoked by the .NET Framework when the
+ /// debugger requests access to the data. The enumerable returned will be traversed by debugging
+ /// utilities to access the tasks currently queued to this scheduler, enabling the debugger to
+ /// provide a representation of this information in the user interface.
+ /// </para>
+ /// <para>
+ /// It is important to note that, when this method is called, all other threads in the process will
+ /// be frozen. Therefore, it's important to avoid synchronization with other threads that may lead to
+ /// blocking. If synchronization is necessary, the method should prefer to throw a <see
+ /// cref="System.NotSupportedException"/>
+ /// than to block, which could cause a debugger to experience delays. Additionally, this method and
+ /// the enumerable returned must not modify any globally visible state.
+ /// </para>
+ /// <para>
+ /// The returned enumerable should never be null. If there are currently no queued tasks, an empty
+ /// enumerable should be returned instead.
+ /// </para>
+ /// <para>
+ /// For developers implementing a custom debugger, this method shouldn't be called directly, but
+ /// rather this functionality should be accessed through the internal wrapper method
+ /// GetScheduledTasksForDebugger:
+ /// <c>internal Task[] GetScheduledTasksForDebugger()</c>. This method returns an array of tasks,
+ /// rather than an enumerable. In order to retrieve a list of active schedulers, a debugger may use
+ /// another internal method: <c>internal static TaskScheduler[] GetTaskSchedulersForDebugger()</c>.
+ /// This static method returns an array of all active TaskScheduler instances.
+ /// GetScheduledTasksForDebugger then may be used on each of these scheduler instances to retrieve
+ /// the list of scheduled tasks for each.
+ /// </para>
+ /// </remarks>
+ /// <returns>An enumerable that allows traversal of tasks currently queued to this scheduler.
+ /// </returns>
+ /// <exception cref="T:System.NotSupportedException">
+ /// This scheduler is unable to generate a list of queued tasks at this time.
+ /// </exception>
+ [SecurityCritical]
+ protected abstract IEnumerable<Task> GetScheduledTasks();
+
+ /// <summary>
+ /// Indicates the maximum concurrency level this
+ /// <see cref="TaskScheduler"/> is able to support.
+ /// </summary>
+ public virtual Int32 MaximumConcurrencyLevel
+ {
+ get
+ {
+ return Int32.MaxValue;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Internal overridable methods
+ //
+
+
+ /// <summary>
+ /// Attempts to execute the target task synchronously.
+ /// </summary>
+ /// <param name="task">The task to run.</param>
+ /// <param name="taskWasPreviouslyQueued">True if the task may have been previously queued,
+ /// false if the task was absolutely not previously queued.</param>
+ /// <returns>True if it ran, false otherwise.</returns>
+ [SecuritySafeCritical]
+ internal bool TryRunInline(Task task, bool taskWasPreviouslyQueued)
+ {
+ // Do not inline unstarted tasks (i.e., task.ExecutingTaskScheduler == null).
+ // Do not inline TaskCompletionSource-style (a.k.a. "promise") tasks.
+ // No need to attempt inlining if the task body was already run (i.e. either TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bits set)
+ TaskScheduler ets = task.ExecutingTaskScheduler;
+
+ // Delegate cross-scheduler inlining requests to target scheduler
+ if(ets != this && ets !=null) return ets.TryRunInline(task, taskWasPreviouslyQueued);
+
+ StackGuard currentStackGuard;
+ if( (ets == null) ||
+ (task.m_action == null) ||
+ task.IsDelegateInvoked ||
+ task.IsCanceled ||
+ (currentStackGuard = Task.CurrentStackGuard).TryBeginInliningScope() == false)
+ {
+ return false;
+ }
+
+ // Task class will still call into TaskScheduler.TryRunInline rather than TryExecuteTaskInline() so that
+ // 1) we can adjust the return code from TryExecuteTaskInline in case a buggy custom scheduler lies to us
+ // 2) we maintain a mechanism for the TLS lookup optimization that we used to have for the ConcRT scheduler (will potentially introduce the same for TP)
+ bool bInlined = false;
+ try
+ {
+ task.FireTaskScheduledIfNeeded(this);
+ bInlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued);
+ }
+ finally
+ {
+ currentStackGuard.EndInliningScope();
+ }
+
+ // If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set
+ // Otherwise the scheduler is buggy
+ if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled))
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_InconsistentStateAfterTryExecuteTaskInline"));
+ }
+
+ return bInlined;
+ }
+
+ /// <summary>
+ /// Attempts to dequeue a <see cref="T:System.Threading.Tasks.Task">Task</see> that was previously queued to
+ /// this scheduler.
+ /// </summary>
+ /// <param name="task">The <see cref="T:System.Threading.Tasks.Task">Task</see> to be dequeued.</param>
+ /// <returns>A Boolean denoting whether the <paramref name="task"/> argument was successfully dequeued.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="task"/> argument is null.</exception>
+ [SecurityCritical]
+ protected internal virtual bool TryDequeue(Task task)
+ {
+ return false;
+ }
+
+ /// <summary>
+ /// Notifies the scheduler that a work item has made progress.
+ /// </summary>
+ internal virtual void NotifyWorkItemProgress()
+ {
+ }
+
+ /// <summary>
+ /// Indicates whether this is a custom scheduler, in which case the safe code paths will be taken upon task entry
+ /// using a CAS to transition from queued state to executing.
+ /// </summary>
+ internal virtual bool RequiresAtomicStartTransition
+ {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Calls QueueTask() after performing any needed firing of events
+ /// </summary>
+ [SecurityCritical]
+ internal void InternalQueueTask(Task task)
+ {
+ Contract.Requires(task != null);
+
+ task.FireTaskScheduledIfNeeded(this);
+
+ this.QueueTask(task);
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Member variables
+ //
+
+ // The global container that keeps track of TaskScheduler instances for debugging purposes.
+ private static ConditionalWeakTable<TaskScheduler, object> s_activeTaskSchedulers;
+
+ // An AppDomain-wide default manager.
+ private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler();
+
+ //static counter used to generate unique TaskScheduler IDs
+ internal static int s_taskSchedulerIdCounter;
+
+ // this TaskScheduler's unique ID
+ private volatile int m_taskSchedulerId;
+
+
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Constructors and public properties
+ //
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.Tasks.TaskScheduler"/>.
+ /// </summary>
+ protected TaskScheduler()
+ {
+ // Register the scheduler in the active scheduler list. This is only relevant when debugging,
+ // so we only pay the cost if the debugger is attached when the scheduler is created. This
+ // means that the internal TaskScheduler.GetTaskSchedulersForDebugger() will only include
+ // schedulers created while the debugger is attached.
+ if (Debugger.IsAttached)
+ {
+ AddToActiveTaskSchedulers();
+ }
+ }
+
+ /// <summary>Adds this scheduler ot the active schedulers tracking collection for debugging purposes.</summary>
+ private void AddToActiveTaskSchedulers()
+ {
+ ConditionalWeakTable<TaskScheduler, object> activeTaskSchedulers = s_activeTaskSchedulers;
+ if (activeTaskSchedulers == null)
+ {
+ Interlocked.CompareExchange(ref s_activeTaskSchedulers, new ConditionalWeakTable<TaskScheduler, object>(), null);
+ activeTaskSchedulers = s_activeTaskSchedulers;
+ }
+ activeTaskSchedulers.Add(this, null);
+ }
+
+ /// <summary>
+ /// Gets the default <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> instance.
+ /// </summary>
+ public static TaskScheduler Default
+ {
+ get
+ {
+ return s_defaultTaskScheduler;
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// associated with the currently executing task.
+ /// </summary>
+ /// <remarks>
+ /// When not called from within a task, <see cref="Current"/> will return the <see cref="Default"/> scheduler.
+ /// </remarks>
+ public static TaskScheduler Current
+ {
+ get
+ {
+ TaskScheduler current = InternalCurrent;
+ return current ?? TaskScheduler.Default;
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// associated with the currently executing task.
+ /// </summary>
+ /// <remarks>
+ /// When not called from within a task, <see cref="InternalCurrent"/> will return null.
+ /// </remarks>
+ internal static TaskScheduler InternalCurrent
+ {
+ get
+ {
+ Task currentTask = Task.InternalCurrent;
+ return ( (currentTask != null)
+ && ((currentTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
+ ) ? currentTask.ExecutingTaskScheduler : null;
+ }
+ }
+
+ /// <summary>
+ /// Creates a <see cref="TaskScheduler"/>
+ /// associated with the current <see cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ /// <remarks>
+ /// All <see cref="System.Threading.Tasks.Task">Task</see> instances queued to
+ /// the returned scheduler will be executed through a call to the
+ /// <see cref="System.Threading.SynchronizationContext.Post">Post</see> method
+ /// on that context.
+ /// </remarks>
+ /// <returns>
+ /// A <see cref="TaskScheduler"/> associated with
+ /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see>, as
+ /// determined by <see cref="System.Threading.SynchronizationContext.Current">SynchronizationContext.Current</see>.
+ /// </returns>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The current SynchronizationContext may not be used as a TaskScheduler.
+ /// </exception>
+ public static TaskScheduler FromCurrentSynchronizationContext()
+ {
+ return new SynchronizationContextTaskScheduler();
+ }
+
+ /// <summary>
+ /// Gets the unique ID for this <see cref="TaskScheduler"/>.
+ /// </summary>
+ public Int32 Id
+ {
+ get
+ {
+ if (m_taskSchedulerId == 0)
+ {
+ int newId = 0;
+
+ // We need to repeat if Interlocked.Increment wraps around and returns 0.
+ // Otherwise next time this scheduler's Id is queried it will get a new value
+ do
+ {
+ newId = Interlocked.Increment(ref s_taskSchedulerIdCounter);
+ } while (newId == 0);
+
+ Interlocked.CompareExchange(ref m_taskSchedulerId, newId, 0);
+ }
+
+ return m_taskSchedulerId;
+ }
+ }
+
+ /// <summary>
+ /// Attempts to execute the provided <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// on this scheduler.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Scheduler implementations are provided with <see cref="T:System.Threading.Tasks.Task">Task</see>
+ /// instances to be executed through either the <see cref="QueueTask"/> method or the
+ /// <see cref="TryExecuteTaskInline"/> method. When the scheduler deems it appropriate to run the
+ /// provided task, <see cref="TryExecuteTask"/> should be used to do so. TryExecuteTask handles all
+ /// aspects of executing a task, including action invocation, exception handling, state management,
+ /// and lifecycle control.
+ /// </para>
+ /// <para>
+ /// <see cref="TryExecuteTask"/> must only be used for tasks provided to this scheduler by the .NET
+ /// Framework infrastructure. It should not be used to execute arbitrary tasks obtained through
+ /// custom mechanisms.
+ /// </para>
+ /// </remarks>
+ /// <param name="task">
+ /// A <see cref="T:System.Threading.Tasks.Task">Task</see> object to be executed.</param>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The <paramref name="task"/> is not associated with this scheduler.
+ /// </exception>
+ /// <returns>A Boolean that is true if <paramref name="task"/> was successfully executed, false if it
+ /// was not. A common reason for execution failure is that the task had previously been executed or
+ /// is in the process of being executed by another thread.</returns>
+ [SecurityCritical]
+ protected bool TryExecuteTask(Task task)
+ {
+ if (task.ExecutingTaskScheduler != this)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_ExecuteTask_WrongTaskScheduler"));
+ }
+
+ return task.ExecuteEntry(true);
+ }
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Events
+ //
+
+ private static EventHandler<UnobservedTaskExceptionEventArgs> _unobservedTaskException;
+ private static readonly object _unobservedTaskExceptionLockObject = new object();
+
+ /// <summary>
+ /// Occurs when a faulted <see cref="System.Threading.Tasks.Task"/>'s unobserved exception is about to trigger exception escalation
+ /// policy, which, by default, would terminate the process.
+ /// </summary>
+ /// <remarks>
+ /// This AppDomain-wide event provides a mechanism to prevent exception
+ /// escalation policy (which, by default, terminates the process) from triggering.
+ /// Each handler is passed a <see cref="T:System.Threading.Tasks.UnobservedTaskExceptionEventArgs"/>
+ /// instance, which may be used to examine the exception and to mark it as observed.
+ /// </remarks>
+ public static event EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException
+ {
+ [System.Security.SecurityCritical]
+ add
+ {
+ if (value != null)
+ {
+ RuntimeHelpers.PrepareContractedDelegate(value);
+ lock (_unobservedTaskExceptionLockObject) _unobservedTaskException += value;
+ }
+ }
+
+ [System.Security.SecurityCritical]
+ remove
+ {
+ lock (_unobservedTaskExceptionLockObject) _unobservedTaskException -= value;
+ }
+ }
+
+
+
+
+
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Internal methods
+ //
+
+ // This is called by the TaskExceptionHolder finalizer.
+ internal static void PublishUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs ueea)
+ {
+ // Lock this logic to prevent just-unregistered handlers from being called.
+ lock (_unobservedTaskExceptionLockObject)
+ {
+ // Since we are under lock, it is technically no longer necessary
+ // to make a copy. It is done here for convenience.
+ EventHandler<UnobservedTaskExceptionEventArgs> handler = _unobservedTaskException;
+ if (handler != null)
+ {
+ handler(sender, ueea);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Provides an array of all queued <see cref="System.Threading.Tasks.Task">Task</see> instances
+ /// for the debugger.
+ /// </summary>
+ /// <remarks>
+ /// The returned array is populated through a call to <see cref="GetScheduledTasks"/>.
+ /// Note that this function is only meant to be invoked by a debugger remotely.
+ /// It should not be called by any other codepaths.
+ /// </remarks>
+ /// <returns>An array of <see cref="System.Threading.Tasks.Task">Task</see> instances.</returns>
+ /// <exception cref="T:System.NotSupportedException">
+ /// This scheduler is unable to generate a list of queued tasks at this time.
+ /// </exception>
+ [SecurityCritical]
+ internal Task[] GetScheduledTasksForDebugger()
+ {
+ // this can throw InvalidOperationException indicating that they are unable to provide the info
+ // at the moment. We should let the debugger receive that exception so that it can indicate it in the UI
+ IEnumerable<Task> activeTasksSource = GetScheduledTasks();
+
+ if (activeTasksSource == null)
+ return null;
+
+ // If it can be cast to an array, use it directly
+ Task[] activeTasksArray = activeTasksSource as Task[];
+ if (activeTasksArray == null)
+ {
+ activeTasksArray = (new List<Task>(activeTasksSource)).ToArray();
+ }
+
+ // touch all Task.Id fields so that the debugger doesn't need to do a lot of cross-proc calls to generate them
+ foreach (Task t in activeTasksArray)
+ {
+ int tmp = t.Id;
+ }
+
+ return activeTasksArray;
+ }
+
+ /// <summary>
+ /// Provides an array of all active <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
+ /// instances for the debugger.
+ /// </summary>
+ /// <remarks>
+ /// This function is only meant to be invoked by a debugger remotely.
+ /// It should not be called by any other codepaths.
+ /// </remarks>
+ /// <returns>An array of <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> instances.</returns>
+ [SecurityCritical]
+ internal static TaskScheduler[] GetTaskSchedulersForDebugger()
+ {
+ if (s_activeTaskSchedulers == null)
+ {
+ // No schedulers were tracked. Just give back the default.
+ return new TaskScheduler[] { s_defaultTaskScheduler };
+ }
+
+ ICollection<TaskScheduler> schedulers = s_activeTaskSchedulers.Keys;
+ if (!schedulers.Contains(s_defaultTaskScheduler))
+ {
+ // Make sure the default is included, in case the debugger attached
+ // after it was created.
+ schedulers.Add(s_defaultTaskScheduler);
+ }
+
+ var arr = new TaskScheduler[schedulers.Count];
+ schedulers.CopyTo(arr, 0);
+ foreach (var scheduler in arr)
+ {
+ Contract.Assert(scheduler != null, "Table returned an incorrect Count or CopyTo failed");
+ int tmp = scheduler.Id; // force Ids for debugger
+ }
+ return arr;
+ }
+
+ /// <summary>
+ /// Nested class that provides debugger view for TaskScheduler
+ /// </summary>
+ internal sealed class SystemThreadingTasks_TaskSchedulerDebugView
+ {
+ private readonly TaskScheduler m_taskScheduler;
+ public SystemThreadingTasks_TaskSchedulerDebugView(TaskScheduler scheduler)
+ {
+ m_taskScheduler = scheduler;
+ }
+
+ // returns the scheduler’s Id
+ public Int32 Id
+ {
+ get { return m_taskScheduler.Id; }
+ }
+
+ // returns the scheduler’s GetScheduledTasks
+ public IEnumerable<Task> ScheduledTasks
+ {
+ [SecurityCritical]
+ get { return m_taskScheduler.GetScheduledTasks(); }
+ }
+ }
+
+ }
+
+
+
+
+ /// <summary>
+ /// A TaskScheduler implementation that executes all tasks queued to it through a call to
+ /// <see cref="System.Threading.SynchronizationContext.Post"/> on the <see cref="T:System.Threading.SynchronizationContext"/>
+ /// that its associated with. The default constructor for this class binds to the current <see cref="T:System.Threading.SynchronizationContext"/>
+ /// </summary>
+ internal sealed class SynchronizationContextTaskScheduler : TaskScheduler
+ {
+ private SynchronizationContext m_synchronizationContext;
+
+ /// <summary>
+ /// Constructs a SynchronizationContextTaskScheduler associated with <see cref="T:System.Threading.SynchronizationContext.Current"/>
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">This constructor expects <see cref="T:System.Threading.SynchronizationContext.Current"/> to be set.</exception>
+ internal SynchronizationContextTaskScheduler()
+ {
+ SynchronizationContext synContext = SynchronizationContext.Current;
+
+ // make sure we have a synccontext to work with
+ if (synContext == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_FromCurrentSynchronizationContext_NoCurrent"));
+ }
+
+ m_synchronizationContext = synContext;
+
+ }
+
+ /// <summary>
+ /// Implemetation of <see cref="T:System.Threading.Tasks.TaskScheduler.QueueTask"/> for this scheduler class.
+ ///
+ /// Simply posts the tasks to be executed on the associated <see cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ /// <param name="task"></param>
+ [SecurityCritical]
+ protected internal override void QueueTask(Task task)
+ {
+ m_synchronizationContext.Post(s_postCallback, (object)task);
+ }
+
+ /// <summary>
+ /// Implementation of <see cref="T:System.Threading.Tasks.TaskScheduler.TryExecuteTaskInline"/> for this scheduler class.
+ ///
+ /// The task will be executed inline only if the call happens within
+ /// the associated <see cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ /// <param name="task"></param>
+ /// <param name="taskWasPreviouslyQueued"></param>
+ [SecurityCritical]
+ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
+ {
+ if (SynchronizationContext.Current == m_synchronizationContext)
+ {
+ return TryExecuteTask(task);
+ }
+ else
+ return false;
+ }
+
+ // not implemented
+ [SecurityCritical]
+ protected override IEnumerable<Task> GetScheduledTasks()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Implementes the <see cref="T:System.Threading.Tasks.TaskScheduler.MaximumConcurrencyLevel"/> property for
+ /// this scheduler class.
+ ///
+ /// By default it returns 1, because a <see cref="T:System.Threading.SynchronizationContext"/> based
+ /// scheduler only supports execution on a single thread.
+ /// </summary>
+ public override Int32 MaximumConcurrencyLevel
+ {
+ get
+ {
+ return 1;
+ }
+ }
+
+ // preallocated SendOrPostCallback delegate
+ private static SendOrPostCallback s_postCallback = new SendOrPostCallback(PostCallback);
+
+ // this is where the actual task invocation occures
+ private static void PostCallback(object obj)
+ {
+ Task task = (Task) obj;
+
+ // calling ExecuteEntry with double execute check enabled because a user implemented SynchronizationContext could be buggy
+ task.ExecuteEntry(true);
+ }
+ }
+
+ /// <summary>
+ /// Provides data for the event that is raised when a faulted <see cref="System.Threading.Tasks.Task"/>'s
+ /// exception goes unobserved.
+ /// </summary>
+ /// <remarks>
+ /// The Exception property is used to examine the exception without marking it
+ /// as observed, whereas the <see cref="SetObserved"/> method is used to mark the exception
+ /// as observed. Marking the exception as observed prevents it from triggering exception escalation policy
+ /// which, by default, terminates the process.
+ /// </remarks>
+ public class UnobservedTaskExceptionEventArgs : EventArgs
+ {
+ private AggregateException m_exception;
+ internal bool m_observed = false;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnobservedTaskExceptionEventArgs"/> class
+ /// with the unobserved exception.
+ /// </summary>
+ /// <param name="exception">The Exception that has gone unobserved.</param>
+ public UnobservedTaskExceptionEventArgs(AggregateException exception) { m_exception = exception; }
+
+ /// <summary>
+ /// Marks the <see cref="Exception"/> as "observed," thus preventing it
+ /// from triggering exception escalation policy which, by default, terminates the process.
+ /// </summary>
+ public void SetObserved() { m_observed = true; }
+
+ /// <summary>
+ /// Gets whether this exception has been marked as "observed."
+ /// </summary>
+ public bool Observed { get { return m_observed; } }
+
+ /// <summary>
+ /// The Exception that went unobserved.
+ /// </summary>
+ public AggregateException Exception { get { return m_exception; } }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs
new file mode 100644
index 0000000000..1d85e49342
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// An exception for task schedulers.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading.Tasks
+{
+
+ /// <summary>
+ /// Represents an exception used to communicate an invalid operation by a
+ /// <see cref="T:System.Threading.Tasks.TaskScheduler"/>.
+ /// </summary>
+ [Serializable]
+ public class TaskSchedulerException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> class.
+ /// </summary>
+ public TaskSchedulerException() : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage")) //
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with a specified error message.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ public TaskSchedulerException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class using the default error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskSchedulerException(Exception innerException)
+ : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage"), innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with a specified error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskSchedulerException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with serialized data.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
+ /// the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
+ /// contains contextual information about the source or destination. </param>
+ protected TaskSchedulerException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs
new file mode 100644
index 0000000000..02b130c297
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs
@@ -0,0 +1,189 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Helper methods for using Tasks to implement the APM pattern.
+//
+// Example usage, wrapping a Task<int>-returning FooAsync method with Begin/EndFoo methods:
+// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state)
+// {
+// Task<int> t = FooAsync(...);
+// return TaskToApm.Begin(t, callback, state);
+// }
+// public int EndFoo(IAsyncResult asyncResult)
+// {
+// return TaskToApm.End<int>(asyncResult);
+// }
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.IO;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern.
+ /// </summary>
+ internal static class TaskToApm
+ {
+ /// <summary>
+ /// Marshals the Task as an IAsyncResult, using the supplied callback and state
+ /// to implement the APM pattern.
+ /// </summary>
+ /// <param name="task">The Task to be marshaled.</param>
+ /// <param name="callback">The callback to be invoked upon completion.</param>
+ /// <param name="state">The state to be stored in the IAsyncResult.</param>
+ /// <returns>An IAsyncResult to represent the task's asynchronous operation.</returns>
+ public static IAsyncResult Begin(Task task, AsyncCallback callback, object state)
+ {
+ Contract.Requires(task != null);
+
+ // If the task has already completed, then since the Task's CompletedSynchronously==false
+ // and we want it to be true, we need to create a new IAsyncResult. (We also need the AsyncState to match.)
+ IAsyncResult asyncResult;
+ if (task.IsCompleted)
+ {
+ // Synchronous completion
+ asyncResult = new TaskWrapperAsyncResult(task, state, completedSynchronously: true);
+ if (callback != null)
+ callback(asyncResult);
+ }
+ // Otherwise, we need to schedule a callback. Whether we can use the Task as the IAsyncResult
+ // depends on whether the Task's AsyncState has reference equality with the requested state.
+ else
+ {
+ // Asynchronous completion
+ asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false);
+ if (callback != null)
+ InvokeCallbackWhenTaskCompletes(task, callback, asyncResult);
+ }
+ return asyncResult;
+ }
+
+ /// <summary>Processes an IAsyncResult returned by Begin.</summary>
+ /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
+ public static void End(IAsyncResult asyncResult)
+ {
+ Task task;
+
+ // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task.
+ var twar = asyncResult as TaskWrapperAsyncResult;
+ if (twar != null)
+ {
+ task = twar.Task;
+ Contract.Assert(task != null, "TaskWrapperAsyncResult should never wrap a null Task.");
+ }
+ // Otherwise, the IAsyncResult should be a Task.
+ else
+ {
+ task = asyncResult as Task;
+ }
+
+ // Make sure we actually got a task, then complete the operation by waiting on it.
+ if (task == null)
+ __Error.WrongAsyncResult();
+ task.GetAwaiter().GetResult();
+ }
+
+ /// <summary>Processes an IAsyncResult returned by Begin.</summary>
+ /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
+ public static TResult End<TResult>(IAsyncResult asyncResult)
+ {
+ Task<TResult> task;
+
+ // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task.
+ var twar = asyncResult as TaskWrapperAsyncResult;
+ if (twar != null)
+ {
+ task = twar.Task as Task<TResult>;
+ Contract.Assert(twar.Task != null, "TaskWrapperAsyncResult should never wrap a null Task.");
+ }
+ // Otherwise, the IAsyncResult should be a Task<TResult>.
+ else
+ {
+ task = asyncResult as Task<TResult>;
+ }
+
+ // Make sure we actually got a task, then complete the operation by waiting on it.
+ if (task == null)
+ __Error.WrongAsyncResult();
+ return task.GetAwaiter().GetResult();
+ }
+
+ /// <summary>Invokes the callback asynchronously when the task has completed.</summary>
+ /// <param name="antecedent">The Task to await.</param>
+ /// <param name="callback">The callback to invoke when the Task completes.</param>
+ /// <param name="asyncResult">The Task used as the IAsyncResult.</param>
+ private static void InvokeCallbackWhenTaskCompletes(Task antecedent, AsyncCallback callback, IAsyncResult asyncResult)
+ {
+ Contract.Requires(antecedent != null);
+ Contract.Requires(callback != null);
+ Contract.Requires(asyncResult != null);
+
+ // We use OnCompleted rather than ContinueWith in order to avoid running synchronously
+ // if the task has already completed by the time we get here. This is separated out into
+ // its own method currently so that we only pay for the closure if necessary.
+ antecedent.ConfigureAwait(continueOnCapturedContext:false)
+ .GetAwaiter()
+ .OnCompleted(() => callback(asyncResult));
+
+ // PERFORMANCE NOTE:
+ // Assuming we're in the default ExecutionContext, the "slow path" of an incomplete
+ // task will result in four allocations: the new IAsyncResult, the delegate+closure
+ // in this method, and the continuation object inside of OnCompleted (necessary
+ // to capture both the Action delegate and the ExecutionContext in a single object).
+ // In the future, if performance requirements drove a need, those four
+ // allocations could be reduced to one. This would be achieved by having TaskWrapperAsyncResult
+ // also implement ITaskCompletionAction (and optionally IThreadPoolWorkItem). It would need
+ // additional fields to store the AsyncCallback and an ExecutionContext. Once configured,
+ // it would be set into the Task as a continuation. Its Invoke method would then be run when
+ // the antecedent completed, and, doing all of the necessary work to flow ExecutionContext,
+ // it would invoke the AsyncCallback. It could also have a field on it for the antecedent,
+ // so that the End method would have access to the completed antecedent. For related examples,
+ // see other implementations of ITaskCompletionAction, and in particular ReadWriteTask
+ // used in Stream.Begin/EndXx's implementation.
+ }
+
+ /// <summary>
+ /// Provides a simple IAsyncResult that wraps a Task. This, in effect, allows
+ /// for overriding what's seen for the CompletedSynchronously and AsyncState values.
+ /// </summary>
+ private sealed class TaskWrapperAsyncResult : IAsyncResult
+ {
+ /// <summary>The wrapped Task.</summary>
+ internal readonly Task Task;
+ /// <summary>The new AsyncState value.</summary>
+ private readonly object m_state;
+ /// <summary>The new CompletedSynchronously value.</summary>
+ private readonly bool m_completedSynchronously;
+
+ /// <summary>Initializes the IAsyncResult with the Task to wrap and the overriding AsyncState and CompletedSynchronously values.</summary>
+ /// <param name="task">The Task to wrap.</param>
+ /// <param name="state">The new AsyncState value</param>
+ /// <param name="completedSynchronously">The new CompletedSynchronously value.</param>
+ internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously)
+ {
+ Contract.Requires(task != null);
+ Contract.Requires(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed.");
+
+ this.Task = task;
+ m_state = state;
+ m_completedSynchronously = completedSynchronously;
+ }
+
+ // The IAsyncResult implementation.
+ // - IsCompleted and AsyncWaitHandle just pass through to the Task.
+ // - AsyncState and CompletedSynchronously return the corresponding values stored in this object.
+
+ object IAsyncResult.AsyncState { get { return m_state; } }
+ bool IAsyncResult.CompletedSynchronously { get { return m_completedSynchronously; } }
+ bool IAsyncResult.IsCompleted { get { return this.Task.IsCompleted; } }
+ WaitHandle IAsyncResult.AsyncWaitHandle { get { return ((IAsyncResult)this.Task).AsyncWaitHandle; } }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs
new file mode 100644
index 0000000000..dd4cbc9a66
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs
@@ -0,0 +1,139 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// TaskScheduler.cs
+//
+//
+// This file contains the primary interface and management of tasks and queues.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Security;
+using System.Diagnostics.Contracts;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// An implementation of TaskScheduler that uses the ThreadPool scheduler
+ /// </summary>
+ internal sealed class ThreadPoolTaskScheduler: TaskScheduler
+ {
+ /// <summary>
+ /// Constructs a new ThreadPool task scheduler object
+ /// </summary>
+ internal ThreadPoolTaskScheduler()
+ {
+ int id = base.Id; // force ID creation of the default scheduler
+ }
+
+ // static delegate for threads allocated to handle LongRunning tasks.
+ private static readonly ParameterizedThreadStart s_longRunningThreadWork = new ParameterizedThreadStart(LongRunningThreadWork);
+
+ private static void LongRunningThreadWork(object obj)
+ {
+ Contract.Requires(obj != null, "TaskScheduler.LongRunningThreadWork: obj is null");
+ Task t = obj as Task;
+ Contract.Assert(t != null, "TaskScheduler.LongRunningThreadWork: t is null");
+ t.ExecuteEntry(false);
+ }
+
+ /// <summary>
+ /// Schedules a task to the ThreadPool.
+ /// </summary>
+ /// <param name="task">The task to schedule.</param>
+ [SecurityCritical]
+ protected internal override void QueueTask(Task task)
+ {
+ if ((task.Options & TaskCreationOptions.LongRunning) != 0)
+ {
+ // Run LongRunning tasks on their own dedicated thread.
+ Thread thread = new Thread(s_longRunningThreadWork);
+ thread.IsBackground = true; // Keep this thread from blocking process shutdown
+ thread.Start(task);
+ }
+ else
+ {
+ // Normal handling for non-LongRunning tasks.
+ bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);
+ ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
+ }
+ }
+
+ /// <summary>
+ /// This internal function will do this:
+ /// (1) If the task had previously been queued, attempt to pop it and return false if that fails.
+ /// (2) Propagate the return value from Task.ExecuteEntry() back to the caller.
+ ///
+ /// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs
+ /// to account for exceptions that need to be propagated, and throw themselves accordingly.
+ /// </summary>
+ [SecurityCritical]
+ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
+ {
+ // If the task was previously scheduled, and we can't pop it, then return false.
+ if (taskWasPreviouslyQueued && !ThreadPool.TryPopCustomWorkItem(task))
+ return false;
+
+ // Propagate the return value of Task.ExecuteEntry()
+ bool rval = false;
+ try
+ {
+ rval = task.ExecuteEntry(false); // handles switching Task.Current etc.
+ }
+ finally
+ {
+ // Only call NWIP() if task was previously queued
+ if(taskWasPreviouslyQueued) NotifyWorkItemProgress();
+ }
+
+ return rval;
+ }
+
+ [SecurityCritical]
+ protected internal override bool TryDequeue(Task task)
+ {
+ // just delegate to TP
+ return ThreadPool.TryPopCustomWorkItem(task);
+ }
+
+ [SecurityCritical]
+ protected override IEnumerable<Task> GetScheduledTasks()
+ {
+ return FilterTasksFromWorkItems(ThreadPool.GetQueuedWorkItems());
+ }
+
+ private IEnumerable<Task> FilterTasksFromWorkItems(IEnumerable<IThreadPoolWorkItem> tpwItems)
+ {
+ foreach (IThreadPoolWorkItem tpwi in tpwItems)
+ {
+ if (tpwi is Task)
+ {
+ yield return (Task)tpwi;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Notifies the scheduler that work is progressing (no-op).
+ /// </summary>
+ internal override void NotifyWorkItemProgress()
+ {
+ ThreadPool.NotifyWorkItemProgress();
+ }
+
+ /// <summary>
+ /// This is the only scheduler that returns false for this property, indicating that the task entry codepath is unsafe (CAS free)
+ /// since we know that the underlying scheduler already takes care of atomic transitions from queued to non-queued.
+ /// </summary>
+ internal override bool RequiresAtomicStartTransition
+ {
+ get { return false; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs
new file mode 100644
index 0000000000..39e6ca1d45
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Tasks/future.cs
@@ -0,0 +1,1667 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A task that produces a value.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using System.Security;
+using System.Security.Permissions;
+using System.Threading;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+// Disable the "reference to volatile field not treated as volatile" error.
+#pragma warning disable 0420
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Represents an asynchronous operation that produces a result at some time in the future.
+ /// </summary>
+ /// <typeparam name="TResult">
+ /// The type of the result produced by this <see cref="Task{TResult}"/>.
+ /// </typeparam>
+ /// <remarks>
+ /// <para>
+ /// <see cref="Task{TResult}"/> instances may be created in a variety of ways. The most common approach is by
+ /// using the task's <see cref="Factory"/> property to retrieve a <see
+ /// cref="System.Threading.Tasks.TaskFactory{TResult}"/> instance that can be used to create tasks for several
+ /// purposes. For example, to create a <see cref="Task{TResult}"/> that runs a function, the factory's StartNew
+ /// method may be used:
+ /// <code>
+ /// // C#
+ /// var t = Task&lt;int&gt;.Factory.StartNew(() => GenerateResult());
+ /// - or -
+ /// var t = Task.Factory.StartNew(() => GenerateResult());
+ ///
+ /// ' Visual Basic
+ /// Dim t = Task&lt;int&gt;.Factory.StartNew(Function() GenerateResult())
+ /// - or -
+ /// Dim t = Task.Factory.StartNew(Function() GenerateResult())
+ /// </code>
+ /// </para>
+ /// <para>
+ /// The <see cref="Task{TResult}"/> class also provides constructors that initialize the task but that do not
+ /// schedule it for execution. For performance reasons, the StartNew method should be the
+ /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation
+ /// and scheduling must be separated, the constructors may be used, and the task's
+ /// <see cref="System.Threading.Tasks.Task.Start()">Start</see>
+ /// method may then be used to schedule the task for execution at a later time.
+ /// </para>
+ /// <para>
+ /// All members of <see cref="Task{TResult}"/>, except for
+ /// <see cref="System.Threading.Tasks.Task.Dispose()">Dispose</see>, are thread-safe
+ /// and may be used from multiple threads concurrently.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [DebuggerTypeProxy(typeof(SystemThreadingTasks_FutureDebugView<>))]
+ [DebuggerDisplay("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}, Result = {DebuggerDisplayResultDescription}")]
+ public class Task<TResult> : Task
+#if SUPPORT_IOBSERVABLE
+ , IObservable<TResult>
+#endif
+ {
+ internal TResult m_result; // The value itself, if set.
+
+ private static readonly TaskFactory<TResult> s_Factory = new TaskFactory<TResult>();
+
+ // Delegate used by:
+ // public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);
+ // public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks);
+ // Used to "cast" from Task<Task> to Task<Task<TResult>>.
+ internal static readonly Func<Task<Task>, Task<TResult>> TaskWhenAnyCast = completed => (Task<TResult>)completed.Result;
+
+ // Construct a promise-style task without any options.
+ internal Task() :
+ base()
+ {
+ }
+
+ // Construct a promise-style task with state and options.
+ internal Task(object state, TaskCreationOptions options) :
+ base(state, options, promiseStyle:true)
+ {
+ }
+
+
+ // Construct a pre-completed Task<TResult>
+ internal Task(TResult result) :
+ base(false, TaskCreationOptions.None, default(CancellationToken))
+ {
+ m_result = result;
+ }
+
+ internal Task(bool canceled, TResult result, TaskCreationOptions creationOptions, CancellationToken ct)
+ : base(canceled, creationOptions, ct)
+ {
+ if (!canceled)
+ {
+ m_result = result;
+ }
+ }
+
+ // Uncomment if/when we want Task.FromException
+ //// Construct a pre-faulted Task<TResult>
+ //internal Task(Exception exception)
+ // : base(exception)
+ //{
+ //}
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified function.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<TResult> function)
+ : this(function, null, default(CancellationToken),
+ TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified function.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to this task.</param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<TResult> function, CancellationToken cancellationToken)
+ : this(function, null, cancellationToken,
+ TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified function and creation options.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<TResult> function, TaskCreationOptions creationOptions)
+ : this(function, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified function and creation options.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<TResult> function, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
+ : this(function, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified function and state.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the action.</param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<object, TResult> function, object state)
+ : this(function, state, null, default(CancellationToken),
+ TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to the new task.</param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<object, TResult> function, object state, CancellationToken cancellationToken)
+ : this(function, state, null, cancellationToken,
+ TaskCreationOptions.None, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the function.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<object, TResult> function, object state, TaskCreationOptions creationOptions)
+ : this(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken),
+ creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options.
+ /// </summary>
+ /// <param name="function">
+ /// The delegate that represents the code to execute in the task. When the function has completed,
+ /// the task's <see cref="Result"/> property will be set to return the result value of the function.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to the new task.</param>
+ /// <param name="creationOptions">
+ /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
+ /// customize the task's behavior.
+ /// </param>
+ /// <exception cref="T:System.ArgumentException">
+ /// The <paramref name="function"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task(Func<object, TResult> function, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
+ : this(function, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken,
+ creationOptions, InternalTaskOptions.None, null)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ internal Task(
+ Func<TResult> valueSelector, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler,
+ ref StackCrawlMark stackMark) :
+ this(valueSelector, parent, cancellationToken,
+ creationOptions, internalOptions, scheduler)
+ {
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a new future object.
+ /// </summary>
+ /// <param name="parent">The parent task for this future.</param>
+ /// <param name="valueSelector">A function that yields the future value.</param>
+ /// <param name="scheduler">The task scheduler which will be used to execute the future.</param>
+ /// <param name="cancellationToken">The CancellationToken for the task.</param>
+ /// <param name="creationOptions">Options to control the future's behavior.</param>
+ /// <param name="internalOptions">Internal options to control the future's behavior.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="creationOptions"/> argument specifies
+ /// a SelfReplicating <see cref="Task{TResult}"/>, which is illegal."/>.</exception>
+ internal Task(Func<TResult> valueSelector, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) :
+ base(valueSelector, null, parent, cancellationToken, creationOptions, internalOptions, scheduler)
+ {
+ if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating);
+ }
+ }
+
+ internal Task(
+ Func<object, TResult> valueSelector, object state, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark) :
+ this(valueSelector, state, parent, cancellationToken, creationOptions, internalOptions, scheduler)
+ {
+ PossiblyCaptureContext(ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a new future object.
+ /// </summary>
+ /// <param name="parent">The parent task for this future.</param>
+ /// <param name="state">An object containing data to be used by the action; may be null.</param>
+ /// <param name="valueSelector">A function that yields the future value.</param>
+ /// <param name="cancellationToken">The CancellationToken for the task.</param>
+ /// <param name="scheduler">The task scheduler which will be used to execute the future.</param>
+ /// <param name="creationOptions">Options to control the future's behavior.</param>
+ /// <param name="internalOptions">Internal options to control the future's behavior.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="creationOptions"/> argument specifies
+ /// a SelfReplicating <see cref="Task{TResult}"/>, which is illegal."/>.</exception>
+ internal Task(Delegate valueSelector, object state, Task parent, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) :
+ base(valueSelector, state, parent, cancellationToken, creationOptions, internalOptions, scheduler)
+ {
+ if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating);
+ }
+ }
+
+
+ // Internal method used by TaskFactory<TResult>.StartNew() methods
+ internal static Task<TResult> StartNew(Task parent, Func<TResult> function, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ if (function == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
+ }
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating);
+ }
+
+ // Create and schedule the future.
+ Task<TResult> f = new Task<TResult>(function, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler, ref stackMark);
+
+ f.ScheduleAndStart(false);
+ return f;
+ }
+
+ // Internal method used by TaskFactory<TResult>.StartNew() methods
+ internal static Task<TResult> StartNew(Task parent, Func<object, TResult> function, object state, CancellationToken cancellationToken,
+ TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark)
+ {
+ if (function == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
+ }
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+ if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating);
+ }
+
+ // Create and schedule the future.
+ Task<TResult> f = new Task<TResult>(function, state, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler, ref stackMark);
+
+ f.ScheduleAndStart(false);
+ return f;
+ }
+
+ // Debugger support
+ private string DebuggerDisplayResultDescription
+ {
+ get
+ {
+ return IsRanToCompletion ? "" + m_result : Environment.GetResourceString("TaskT_DebuggerNoResult");
+ }
+ }
+
+ // Debugger support
+ private string DebuggerDisplayMethodDescription
+ {
+ get
+ {
+ Delegate d = (Delegate)m_action;
+ return d != null ? d.Method.ToString() : "{null}";
+ }
+ }
+
+
+ // internal helper function breaks out logic used by TaskCompletionSource
+ internal bool TrySetResult(TResult result)
+ {
+ if (IsCompleted) return false;
+ Contract.Assert(m_action == null, "Task<T>.TrySetResult(): non-null m_action");
+
+ // "Reserve" the completion for this task, while making sure that: (1) No prior reservation
+ // has been made, (2) The result has not already been set, (3) An exception has not previously
+ // been recorded, and (4) Cancellation has not been requested.
+ //
+ // If the reservation is successful, then set the result and finish completion processing.
+ if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED,
+ TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
+ {
+ m_result = result;
+
+ // Signal completion, for waiting tasks
+
+ // This logic used to be:
+ // Finish(false);
+ // However, that goes through a windy code path, involves many non-inlineable functions
+ // and which can be summarized more concisely with the following snippet from
+ // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult.
+ Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION);
+
+ var cp = m_contingentProperties;
+ if (cp != null) cp.SetCompleted();
+
+ FinishStageThree();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // Transitions the promise task into a successfully completed state with the specified result.
+ // This is dangerous, as no synchronization is used, and thus must only be used
+ // before this task is handed out to any consumers, before any continuations are hooked up,
+ // before its wait handle is accessed, etc. It's use is limited to places like in FromAsync
+ // where the operation completes synchronously, and thus we know we can forcefully complete
+ // the task, avoiding expensive completion paths, before the task is actually given to anyone.
+ internal void DangerousSetResult(TResult result)
+ {
+ Contract.Assert(!IsCompleted, "The promise must not yet be completed.");
+
+ // If we have a parent, we need to notify it of the completion. Take the slow path to handle that.
+ if (m_contingentProperties?.m_parent != null)
+ {
+ bool success = TrySetResult(result);
+
+ // Nobody else has had a chance to complete this Task yet, so we should succeed.
+ Contract.Assert(success);
+ }
+ else
+ {
+ m_result = result;
+ m_stateFlags |= TASK_STATE_RAN_TO_COMPLETION;
+ }
+ }
+
+ /// <summary>
+ /// Gets the result value of this <see cref="Task{TResult}"/>.
+ /// </summary>
+ /// <remarks>
+ /// The get accessor for this property ensures that the asynchronous operation is complete before
+ /// returning. Once the result of the computation is available, it is stored and will be returned
+ /// immediately on later calls to <see cref="Result"/>.
+ /// </remarks>
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ public TResult Result
+ {
+ get { return IsWaitNotificationEnabledOrNotRanToCompletion ? GetResultCore(waitCompletionNotification: true) : m_result; }
+ }
+
+ /// <summary>
+ /// Gets the result value of this <see cref="Task{TResult}"/> once the task has completed successfully.
+ /// </summary>
+ /// <remarks>
+ /// This version of Result should only be used if the task completed successfully and if there's
+ /// no debugger wait notification enabled for this task.
+ /// </remarks>
+ internal TResult ResultOnSuccess
+ {
+ get
+ {
+ Contract.Assert(!IsWaitNotificationEnabledOrNotRanToCompletion,
+ "Should only be used when the task completed successfully and there's no wait notification enabled");
+ return m_result;
+ }
+ }
+
+ // Implements Result. Result delegates to this method if the result isn't already available.
+ internal TResult GetResultCore(bool waitCompletionNotification)
+ {
+ // If the result has not been calculated yet, wait for it.
+ if (!IsCompleted) InternalWait(Timeout.Infinite, default(CancellationToken)); // won't throw if task faulted or canceled; that's handled below
+
+ // Notify the debugger of the wait completion if it's requested such a notification
+ if (waitCompletionNotification) NotifyDebuggerOfWaitCompletionIfNecessary();
+
+ // Throw an exception if appropriate.
+ if (!IsRanToCompletion) ThrowIfExceptional(includeTaskCanceledExceptions: true);
+
+ // We shouldn't be here if the result has not been set.
+ Contract.Assert(IsRanToCompletion, "Task<T>.Result getter: Expected result to have been set.");
+
+ return m_result;
+ }
+
+ // Allow multiple exceptions to be assigned to a promise-style task.
+ // This is useful when a TaskCompletionSource<T> stands in as a proxy
+ // for a "real" task (as we do in Unwrap(), ContinueWhenAny() and ContinueWhenAll())
+ // and the "real" task ends up with multiple exceptions, which is possible when
+ // a task has children.
+ //
+ // Called from TaskCompletionSource<T>.SetException(IEnumerable<Exception>).
+ internal bool TrySetException(object exceptionObject)
+ {
+ Contract.Assert(m_action == null, "Task<T>.TrySetException(): non-null m_action");
+
+ // TCS.{Try}SetException() should have checked for this
+ Contract.Assert(exceptionObject != null, "Expected non-null exceptionObject argument");
+
+ // Only accept these types.
+ Contract.Assert(
+ (exceptionObject is Exception) || (exceptionObject is IEnumerable<Exception>) ||
+ (exceptionObject is ExceptionDispatchInfo) || (exceptionObject is IEnumerable<ExceptionDispatchInfo>),
+ "Expected exceptionObject to be either Exception, ExceptionDispatchInfo, or IEnumerable<> of one of those");
+
+ bool returnValue = false;
+
+ // "Reserve" the completion for this task, while making sure that: (1) No prior reservation
+ // has been made, (2) The result has not already been set, (3) An exception has not previously
+ // been recorded, and (4) Cancellation has not been requested.
+ //
+ // If the reservation is successful, then add the exception(s) and finish completion processing.
+ //
+ // The lazy initialization may not be strictly necessary, but I'd like to keep it here
+ // anyway. Some downstream logic may depend upon an inflated m_contingentProperties.
+ EnsureContingentPropertiesInitialized();
+ if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED,
+ TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
+ {
+ AddException(exceptionObject); // handles singleton exception or exception collection
+ Finish(false);
+ returnValue = true;
+ }
+
+ return returnValue;
+
+ }
+
+ // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder
+ // If the tokenToRecord is not None, it will be stored onto the task.
+ // This method is only valid for promise tasks.
+ internal bool TrySetCanceled(CancellationToken tokenToRecord)
+ {
+ return TrySetCanceled(tokenToRecord, null);
+ }
+
+ // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder
+ // If the tokenToRecord is not None, it will be stored onto the task.
+ // If the OperationCanceledException is not null, it will be stored into the task's exception holder.
+ // This method is only valid for promise tasks.
+ internal bool TrySetCanceled(CancellationToken tokenToRecord, object cancellationException)
+ {
+ Contract.Assert(m_action == null, "Task<T>.TrySetCanceled(): non-null m_action");
+#if DEBUG
+ var ceAsEdi = cancellationException as ExceptionDispatchInfo;
+ Contract.Assert(
+ cancellationException == null ||
+ cancellationException is OperationCanceledException ||
+ (ceAsEdi != null && ceAsEdi.SourceException is OperationCanceledException),
+ "Expected null or an OperationCanceledException");
+#endif
+
+ bool returnValue = false;
+
+ // "Reserve" the completion for this task, while making sure that: (1) No prior reservation
+ // has been made, (2) The result has not already been set, (3) An exception has not previously
+ // been recorded, and (4) Cancellation has not been requested.
+ //
+ // If the reservation is successful, then record the cancellation and finish completion processing.
+ //
+ // Note: I had to access static Task variables through Task<object>
+ // instead of Task, because I have a property named Task and that
+ // was confusing the compiler.
+ if (AtomicStateUpdate(Task<object>.TASK_STATE_COMPLETION_RESERVED,
+ Task<object>.TASK_STATE_COMPLETION_RESERVED | Task<object>.TASK_STATE_CANCELED |
+ Task<object>.TASK_STATE_FAULTED | Task<object>.TASK_STATE_RAN_TO_COMPLETION))
+ {
+ RecordInternalCancellationRequest(tokenToRecord, cancellationException);
+ CancellationCleanupLogic(); // perform cancellation cleanup actions
+ returnValue = true;
+ }
+
+ return returnValue;
+ }
+
+ /// <summary>
+ /// Provides access to factory methods for creating <see cref="Task{TResult}"/> instances.
+ /// </summary>
+ /// <remarks>
+ /// The factory returned from <see cref="Factory"/> is a default instance
+ /// of <see cref="System.Threading.Tasks.TaskFactory{TResult}"/>, as would result from using
+ /// the default constructor on the factory type.
+ /// </remarks>
+ public new static TaskFactory<TResult> Factory { get { return s_Factory; } }
+
+ /// <summary>
+ /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
+ /// </summary>
+ internal override void InnerInvoke()
+ {
+ // Invoke the delegate
+ Contract.Assert(m_action != null);
+ var func = m_action as Func<TResult>;
+ if (func != null)
+ {
+ m_result = func();
+ return;
+ }
+ var funcWithState = m_action as Func<object, TResult>;
+ if (funcWithState != null)
+ {
+ m_result = funcWithState(m_stateObject);
+ return;
+ }
+ Contract.Assert(false, "Invalid m_action in Task<TResult>");
+ }
+
+ #region Await Support
+
+ /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
+ /// <returns>An awaiter instance.</returns>
+ /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
+ public new TaskAwaiter<TResult> GetAwaiter()
+ {
+ return new TaskAwaiter<TResult>(this);
+ }
+
+ /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
+ /// </param>
+ /// <returns>An object used to await this task.</returns>
+ public new ConfiguredTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
+ {
+ return new ConfiguredTaskAwaitable<TResult>(this, continueOnCapturedContext);
+ }
+
+ #endregion
+
+ #region Continuation methods
+
+ #region Action<Task<TResult>> continuations
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>> continuationAction)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>> continuationAction, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>> continuationAction, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>> continuationAction, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>> continuationAction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, only with a stack mark.
+ internal Task ContinueWith(Action<Task<TResult>> continuationAction, TaskScheduler scheduler, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ if (continuationAction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
+ }
+
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(
+ continuationOptions,
+ out creationOptions,
+ out internalOptions);
+
+ Task continuationTask = new ContinuationTaskFromResultTask<TResult>(
+ this, continuationAction, null,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+ #endregion
+
+ #region Action<Task<TResult>, Object> continuations
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the continuation criteria specified through the <paramref
+ /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
+ /// instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <param name="continuationAction">
+ /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation action.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
+ /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
+ /// are not met, the continuation task will be canceled instead of scheduled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationAction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, only with a stack mark.
+ internal Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskScheduler scheduler, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ if (continuationAction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
+ }
+
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(
+ continuationOptions,
+ out creationOptions,
+ out internalOptions);
+
+ Task continuationTask = new ContinuationTaskFromResultTask<TResult>(
+ this, continuationAction, state,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
+
+ return continuationTask;
+ }
+
+ #endregion
+
+ #region Func<Task<TResult>,TNewResult> continuations
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task as an argument.
+ /// </param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// <para>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </para>
+ /// <para>
+ /// The <paramref name="continuationFunction"/>, when executed, should return a <see
+ /// cref="Task{TNewResult}"/>. This task's completion state will be transferred to the task returned
+ /// from the ContinueWith call.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be passed as
+ /// an argument this completed task.
+ /// </param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// <para>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </para>
+ /// <para>
+ /// The <paramref name="continuationFunction"/>, when executed, should return a <see cref="Task{TNewResult}"/>.
+ /// This task's completion state will be transferred to the task returned from the
+ /// ContinueWith call.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, CancellationToken cancellationToken,
+ TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark.
+ internal Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskScheduler scheduler,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ if (continuationFunction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ }
+
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(
+ continuationOptions,
+ out creationOptions,
+ out internalOptions);
+
+ Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>(
+ this, continuationFunction, null,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationFuture, scheduler, cancellationToken, continuationOptions);
+
+ return continuationFuture;
+ }
+ #endregion
+
+ #region Func<Task<TResult>, Object,TNewResult> continuations
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,
+ CancellationToken cancellationToken)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,
+ TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// <para>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current
+ /// task has completed, whether it completes due to running to completion successfully, faulting due
+ /// to an unhandled exception, or exiting out early due to being canceled.
+ /// </para>
+ /// <para>
+ /// The <paramref name="continuationFunction"/>, when executed, should return a <see
+ /// cref="Task{TNewResult}"/>. This task's completion state will be transferred to the task returned
+ /// from the ContinueWith call.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,
+ TaskContinuationOptions continuationOptions)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
+ }
+
+ /// <summary>
+ /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes.
+ /// </summary>
+ /// <typeparam name="TNewResult">
+ /// The type of the result produced by the continuation.
+ /// </typeparam>
+ /// <param name="continuationFunction">
+ /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be
+ /// passed the completed task and the caller-supplied state object as arguments.
+ /// </param>
+ /// <param name="state">An object representing data to be used by the continuation function.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
+ /// <param name="continuationOptions">
+ /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
+ /// as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
+ /// well as execution options, such as <see
+ /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
+ /// </param>
+ /// <param name="scheduler">
+ /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
+ /// execution.
+ /// </param>
+ /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns>
+ /// <remarks>
+ /// <para>
+ /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has
+ /// completed, whether it completes due to running to completion successfully, faulting due to an
+ /// unhandled exception, or exiting out early due to being canceled.
+ /// </para>
+ /// <para>
+ /// The <paramref name="continuationFunction"/>, when executed, should return a <see cref="Task{TNewResult}"/>.
+ /// This task's completion state will be transferred to the task returned from the
+ /// ContinueWith call.
+ /// </para>
+ /// </remarks>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="continuationFunction"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="scheduler"/> argument is null.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
+ /// has already been disposed.
+ /// </exception>
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,
+ CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return ContinueWith<TNewResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
+ }
+
+ // Same as the above overload, just with a stack mark.
+ internal Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,
+ TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
+ {
+ if (continuationFunction == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
+ }
+
+ if (scheduler == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
+ }
+
+ TaskCreationOptions creationOptions;
+ InternalTaskOptions internalOptions;
+ CreationOptionsFromContinuationOptions(
+ continuationOptions,
+ out creationOptions,
+ out internalOptions);
+
+ Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>(
+ this, continuationFunction, state,
+ creationOptions, internalOptions,
+ ref stackMark
+ );
+
+ // Register the continuation. If synchronous execution is requested, this may
+ // actually invoke the continuation before returning.
+ ContinueWithCore(continuationFuture, scheduler, cancellationToken, continuationOptions);
+
+ return continuationFuture;
+ }
+
+ #endregion
+
+ #endregion
+
+ /// <summary>
+ /// Subscribes an <see cref="IObserver{TResult}"/> to receive notification of the final state of this <see cref="Task{TResult}"/>.
+ /// </summary>
+ /// <param name="observer">
+ /// The <see cref="IObserver{TResult}"/> to call on task completion. If this Task throws an exception,
+ /// observer.OnError is called with this Task's AggregateException. If this Task RanToCompletion,
+ /// observer.OnNext is called with this Task's result, followed by a call to observer.OnCompleted.
+ /// If this Task is Canceled, observer.OnError is called with a TaskCanceledException
+ /// containing this Task's CancellationToken
+ /// </param>
+ /// <returns>An IDisposable object <see cref="Task"/>.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="observer"/> argument is null.
+ /// </exception>
+#if SUPPORT_IOBSERVABLE
+ IDisposable IObservable<TResult>.Subscribe(IObserver<TResult> observer)
+ {
+ if (observer == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.observer);
+
+
+ var continuationTask =
+ this.ContinueWith(delegate(Task<TResult> observedTask, object taskObserverObject)
+ {
+ IObserver<TResult> taskObserver = (IObserver<TResult>)taskObserverObject;
+ if (observedTask.IsFaulted)
+ taskObserver.OnError(observedTask.Exception);
+ else if (observedTask.IsCanceled)
+ taskObserver.OnError(new TaskCanceledException(observedTask));
+ else
+ {
+ taskObserver.OnNext(observedTask.Result);
+ taskObserver.OnCompleted();
+ }
+
+ }, observer, TaskScheduler.Default);
+
+ return new DisposableSubscription(this, continuationTask);
+ }
+#endif
+ }
+
+#if SUPPORT_IOBSERVABLE
+ // Class that calls RemoveContinuation if Dispose() is called before task completion
+ internal class DisposableSubscription : IDisposable
+ {
+ private Task _notifyObserverContinuationTask;
+ private Task _observedTask;
+
+ internal DisposableSubscription(Task observedTask, Task notifyObserverContinuationTask)
+ {
+ _observedTask = observedTask;
+ _notifyObserverContinuationTask = notifyObserverContinuationTask;
+ }
+ void IDisposable.Dispose()
+ {
+ Task localObservedTask = _observedTask;
+ Task localNotifyingContinuationTask = _notifyObserverContinuationTask;
+ if (localObservedTask != null && localNotifyingContinuationTask != null && !localObservedTask.IsCompleted)
+ {
+ localObservedTask.RemoveContinuation(localNotifyingContinuationTask);
+ }
+ _observedTask = null;
+ _notifyObserverContinuationTask = null;
+ }
+ }
+#endif
+
+ // Proxy class for better debugging experience
+ internal class SystemThreadingTasks_FutureDebugView<TResult>
+ {
+ private Task<TResult> m_task;
+
+ public SystemThreadingTasks_FutureDebugView(Task<TResult> task)
+ {
+ m_task = task;
+ }
+
+ public TResult Result { get { return m_task.Status == TaskStatus.RanToCompletion ? m_task.Result : default(TResult); } }
+ public object AsyncState { get { return m_task.AsyncState; } }
+ public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } }
+ public Exception Exception { get { return m_task.Exception; } }
+ public int Id { get { return m_task.Id; } }
+ public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } }
+ public TaskStatus Status { get { return m_task.Status; } }
+
+
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Thread.cs b/src/mscorlib/src/System/Threading/Thread.cs
new file mode 100644
index 0000000000..e62cfae9fe
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Thread.cs
@@ -0,0 +1,1756 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Class for creating and managing a thread.
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+ using System.Threading;
+ using System.Runtime;
+ using System.Runtime.InteropServices;
+#if FEATURE_REMOTING
+ using System.Runtime.Remoting.Contexts;
+ using System.Runtime.Remoting.Messaging;
+#endif
+ using System;
+ using System.Diagnostics;
+ using System.Security.Permissions;
+ using System.Security.Principal;
+ using System.Globalization;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Security;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ internal delegate Object InternalCrossContextDelegate(Object[] args);
+
+ internal class ThreadHelper
+ {
+ [System.Security.SecuritySafeCritical]
+ static ThreadHelper() {}
+
+ Delegate _start;
+ Object _startArg = null;
+ ExecutionContext _executionContext = null;
+ internal ThreadHelper(Delegate start)
+ {
+ _start = start;
+ }
+
+ internal void SetExecutionContextHelper(ExecutionContext ec)
+ {
+ _executionContext = ec;
+ }
+
+ [System.Security.SecurityCritical]
+ static internal ContextCallback _ccb = new ContextCallback(ThreadStart_Context);
+
+ [System.Security.SecurityCritical]
+ static private void ThreadStart_Context(Object state)
+ {
+ ThreadHelper t = (ThreadHelper)state;
+ if (t._start is ThreadStart)
+ {
+ ((ThreadStart)t._start)();
+ }
+ else
+ {
+ ((ParameterizedThreadStart)t._start)(t._startArg);
+ }
+ }
+
+ // call back helper
+ #if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+ #else
+ [System.Security.SecurityCritical]
+ #endif
+ internal void ThreadStart(object obj)
+ {
+ _startArg = obj;
+ if (_executionContext != null)
+ {
+ ExecutionContext.Run(_executionContext, _ccb, (Object)this);
+ }
+ else
+ {
+ ((ParameterizedThreadStart)_start)(obj);
+ }
+ }
+
+ // call back helper
+ #if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+ #else
+ [System.Security.SecurityCritical]
+ #endif
+ internal void ThreadStart()
+ {
+ if (_executionContext != null)
+ {
+ ExecutionContext.Run(_executionContext, _ccb, (Object)this);
+ }
+ else
+ {
+ ((ThreadStart)_start)();
+ }
+ }
+ }
+
+ internal struct ThreadHandle
+ {
+ private IntPtr m_ptr;
+
+ internal ThreadHandle(IntPtr pThread)
+ {
+ m_ptr = pThread;
+ }
+ }
+
+ // deliberately not [serializable]
+ [ClassInterface(ClassInterfaceType.None)]
+ [ComDefaultInterface(typeof(_Thread))]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class Thread : CriticalFinalizerObject, _Thread
+ {
+ /*=========================================================================
+ ** Data accessed from managed code that needs to be defined in
+ ** ThreadBaseObject to maintain alignment between the two classes.
+ ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h
+ =========================================================================*/
+#if FEATURE_REMOTING
+ private Context m_Context;
+#endif
+ private ExecutionContext m_ExecutionContext; // this call context follows the logical thread
+#if FEATURE_CORECLR
+ private SynchronizationContext m_SynchronizationContext; // On CoreCLR, this is maintained separately from ExecutionContext
+#endif
+
+ private String m_Name;
+ private Delegate m_Delegate; // Delegate
+
+#if FEATURE_LEAK_CULTURE_INFO
+ private CultureInfo m_CurrentCulture;
+ private CultureInfo m_CurrentUICulture;
+#endif
+ private Object m_ThreadStartArg;
+
+ /*=========================================================================
+ ** The base implementation of Thread is all native. The following fields
+ ** should never be used in the C# code. They are here to define the proper
+ ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS
+ ** YOU MODIFY ThreadBaseObject in vm\object.h
+ =========================================================================*/
+#pragma warning disable 169
+#pragma warning disable 414 // These fields are not used from managed.
+ // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit
+ // fields on 64-bit platforms, where they will be sorted together.
+
+ private IntPtr DONT_USE_InternalThread; // Pointer
+ private int m_Priority; // INT32
+ private int m_ManagedThreadId; // INT32
+
+#pragma warning restore 414
+#pragma warning restore 169
+
+ private bool m_ExecutionContextBelongsToOuterScope;
+#if DEBUG
+ private bool m_ForbidExecutionContextMutation;
+#endif
+
+ /*=========================================================================
+ ** This manager is responsible for storing the global data that is
+ ** shared amongst all the thread local stores.
+ =========================================================================*/
+ static private LocalDataStoreMgr s_LocalDataStoreMgr;
+
+ /*=========================================================================
+ ** Thread-local data store
+ =========================================================================*/
+ [ThreadStatic]
+ static private LocalDataStoreHolder s_LocalDataStore;
+
+ // Do not move! Order of above fields needs to be preserved for alignment
+ // with native code
+ // See code:#threadCultureInfo
+#if !FEATURE_LEAK_CULTURE_INFO
+ [ThreadStatic]
+ internal static CultureInfo m_CurrentCulture;
+ [ThreadStatic]
+ internal static CultureInfo m_CurrentUICulture;
+#endif
+
+ static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture;
+ static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture;
+
+ static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args)
+ {
+#if FEATURE_LEAK_CULTURE_INFO
+ Thread.CurrentThread.m_CurrentCulture = args.CurrentValue;
+#else
+ m_CurrentCulture = args.CurrentValue;
+#endif // FEATURE_LEAK_CULTURE_INFO
+ }
+
+ static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args)
+ {
+#if FEATURE_LEAK_CULTURE_INFO
+ Thread.CurrentThread.m_CurrentUICulture = args.CurrentValue;
+#else
+ m_CurrentUICulture = args.CurrentValue;
+#endif // FEATURE_LEAK_CULTURE_INFO
+ }
+
+#if FEATURE_CORECLR
+ // Adding an empty default ctor for annotation purposes
+ [System.Security.SecuritySafeCritical] // auto-generated
+ internal Thread(){}
+#endif // FEATURE_CORECLR
+
+ /*=========================================================================
+ ** Creates a new Thread object which will begin execution at
+ ** start.ThreadStart on a new thread when the Start method is called.
+ **
+ ** Exceptions: ArgumentNullException if start == null.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public Thread(ThreadStart start) {
+ if (start == null) {
+ throw new ArgumentNullException("start");
+ }
+ Contract.EndContractBlock();
+ SetStartHelper((Delegate)start,0); //0 will setup Thread with default stackSize
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public Thread(ThreadStart start, int maxStackSize) {
+ if (start == null) {
+ throw new ArgumentNullException("start");
+ }
+ if (0 > maxStackSize)
+ throw new ArgumentOutOfRangeException("maxStackSize",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ SetStartHelper((Delegate)start, maxStackSize);
+ }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public Thread(ParameterizedThreadStart start) {
+ if (start == null) {
+ throw new ArgumentNullException("start");
+ }
+ Contract.EndContractBlock();
+ SetStartHelper((Delegate)start, 0);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public Thread(ParameterizedThreadStart start, int maxStackSize) {
+ if (start == null) {
+ throw new ArgumentNullException("start");
+ }
+ if (0 > maxStackSize)
+ throw new ArgumentOutOfRangeException("maxStackSize",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ SetStartHelper((Delegate)start, maxStackSize);
+ }
+
+ [ComVisible(false)]
+ public override int GetHashCode()
+ {
+ return m_ManagedThreadId;
+ }
+
+ extern public int ManagedThreadId
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get;
+ }
+
+ // Returns handle for interop with EE. The handle is guaranteed to be non-null.
+ internal unsafe ThreadHandle GetNativeHandle()
+ {
+ IntPtr thread = DONT_USE_InternalThread;
+
+ // This should never happen under normal circumstances. m_assembly is always assigned before it is handed out to the user.
+ // There are ways how to create an unitialized objects through remoting, etc. Avoid AVing in the EE by throwing a nice
+ // exception here.
+ if (thread.IsNull())
+ throw new ArgumentException(null, Environment.GetResourceString("Argument_InvalidHandle"));
+
+ return new ThreadHandle(thread);
+ }
+
+
+ /*=========================================================================
+ ** Spawns off a new thread which will begin executing at the ThreadStart
+ ** method on the IThreadable interface passed in the constructor. Once the
+ ** thread is dead, it cannot be restarted with another call to Start.
+ **
+ ** Exceptions: ThreadStateException if the thread has already been started.
+ =========================================================================*/
+ [HostProtection(Synchronization=true,ExternalThreading=true)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public void Start()
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Start(ref stackMark);
+ }
+
+ [HostProtection(Synchronization=true,ExternalThreading=true)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public void Start(object parameter)
+ {
+ //In the case of a null delegate (second call to start on same thread)
+ // StartInternal method will take care of the error reporting
+ if(m_Delegate is ThreadStart)
+ {
+ //We expect the thread to be setup with a ParameterizedThreadStart
+ // if this constructor is called.
+ //If we got here then that wasn't the case
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ThreadWrongThreadStart"));
+ }
+ m_ThreadStartArg = parameter;
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Start(ref stackMark);
+ }
+
+ [System.Security.SecuritySafeCritical]
+ private void Start(ref StackCrawlMark stackMark)
+ {
+#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
+ // Eagerly initialize the COM Apartment state of the thread if we're allowed to.
+ StartupSetApartmentStateInternal();
+#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+ // Attach current thread's security principal object to the new
+ // thread. Be careful not to bind the current thread to a principal
+ // if it's not already bound.
+ if (m_Delegate != null)
+ {
+ // If we reach here with a null delegate, something is broken. But we'll let the StartInternal method take care of
+ // reporting an error. Just make sure we dont try to dereference a null delegate.
+ ThreadHelper t = (ThreadHelper)(m_Delegate.Target);
+ ExecutionContext ec = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx);
+ t.SetExecutionContextHelper(ec);
+ }
+#if FEATURE_IMPERSONATION
+ IPrincipal principal = (IPrincipal)CallContext.Principal;
+#else
+ IPrincipal principal = null;
+#endif
+ StartInternal(principal, ref stackMark);
+ }
+
+
+#if FEATURE_CORECLR
+ internal ExecutionContext ExecutionContext
+ {
+ get { return m_ExecutionContext; }
+ set { m_ExecutionContext = value; }
+ }
+
+ internal SynchronizationContext SynchronizationContext
+ {
+ get { return m_SynchronizationContext; }
+ set { m_SynchronizationContext = value; }
+ }
+#else // !FEATURE_CORECLR
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal ExecutionContext.Reader GetExecutionContextReader()
+ {
+ return new ExecutionContext.Reader(m_ExecutionContext);
+ }
+
+ internal bool ExecutionContextBelongsToCurrentScope
+ {
+ get { return !m_ExecutionContextBelongsToOuterScope; }
+ set { m_ExecutionContextBelongsToOuterScope = !value; }
+ }
+
+#if DEBUG
+ internal bool ForbidExecutionContextMutation
+ {
+ set { m_ForbidExecutionContextMutation = value; }
+ }
+#endif
+
+ // note: please don't access this directly from mscorlib. Use GetMutableExecutionContext or GetExecutionContextReader instead.
+ public ExecutionContext ExecutionContext
+ {
+ [SecuritySafeCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ get
+ {
+ ExecutionContext result;
+ if (this == Thread.CurrentThread)
+ result = GetMutableExecutionContext();
+ else
+ result = m_ExecutionContext;
+
+ return result;
+ }
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal ExecutionContext GetMutableExecutionContext()
+ {
+ Contract.Assert(Thread.CurrentThread == this);
+#if DEBUG
+ Contract.Assert(!m_ForbidExecutionContextMutation);
+#endif
+ if (m_ExecutionContext == null)
+ {
+ m_ExecutionContext = new ExecutionContext();
+ }
+ else if (!ExecutionContextBelongsToCurrentScope)
+ {
+ ExecutionContext copy = m_ExecutionContext.CreateMutableCopy();
+ m_ExecutionContext = copy;
+ }
+
+ ExecutionContextBelongsToCurrentScope = true;
+ return m_ExecutionContext;
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal void SetExecutionContext(ExecutionContext value, bool belongsToCurrentScope)
+ {
+ m_ExecutionContext = value;
+ ExecutionContextBelongsToCurrentScope = belongsToCurrentScope;
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal void SetExecutionContext(ExecutionContext.Reader value, bool belongsToCurrentScope)
+ {
+ m_ExecutionContext = value.DangerousGetRawExecutionContext();
+ ExecutionContextBelongsToCurrentScope = belongsToCurrentScope;
+ }
+#endif //!FEATURE_CORECLR
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void StartInternal(IPrincipal principal, ref StackCrawlMark stackMark);
+#if FEATURE_COMPRESSEDSTACK
+ /// <internalonly/>
+ [System.Security.SecurityCritical] // auto-generated_required
+ [DynamicSecurityMethodAttribute()]
+ [Obsolete("Thread.SetCompressedStack is no longer supported. Please use the System.Threading.CompressedStack class")]
+ public void SetCompressedStack( CompressedStack stack )
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ThreadAPIsNotSupported"));
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal extern IntPtr SetAppDomainStack( SafeCompressedStackHandle csHandle);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ internal extern void RestoreAppDomainStack( IntPtr appDomainStack);
+
+
+ /// <internalonly/>
+ [System.Security.SecurityCritical] // auto-generated_required
+ [Obsolete("Thread.GetCompressedStack is no longer supported. Please use the System.Threading.CompressedStack class")]
+ public CompressedStack GetCompressedStack()
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ThreadAPIsNotSupported"));
+ }
+#endif // #if FEATURE_COMPRESSEDSTACK
+
+
+ // Helper method to get a logical thread ID for StringBuilder (for
+ // correctness) and for FileStream's async code path (for perf, to
+ // avoid creating a Thread instance).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr InternalGetCurrentThread();
+
+ /*=========================================================================
+ ** Raises a ThreadAbortException in the thread, which usually
+ ** results in the thread's death. The ThreadAbortException is a special
+ ** exception that is not catchable. The finally clauses of all try
+ ** statements will be executed before the thread dies. This includes the
+ ** finally that a thread might be executing at the moment the Abort is raised.
+ ** The thread is not stopped immediately--you must Join on the
+ ** thread to guarantee it has stopped.
+ ** It is possible for a thread to do an unbounded amount of computation in
+ ** the finally's and thus indefinitely delay the threads death.
+ ** If Abort() is called on a thread that has not been started, the thread
+ ** will abort when Start() is called.
+ ** If Abort is called twice on the same thread, a DuplicateThreadAbort
+ ** exception is thrown.
+ =========================================================================*/
+
+#if !FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread=true)]
+ public void Abort(Object stateInfo)
+ {
+ // If two aborts come at the same time, it is possible that the state info
+ // gets set by one, and the actual abort gets delivered by another. But this
+ // is not distinguishable by an application.
+ // The accessor helper will only set the value if it isn't already set,
+ // and that particular bit of native code can test much faster than this
+ // code could, because testing might cause a cross-appdomain marshalling.
+ AbortReason = stateInfo;
+
+ // Note: we demand ControlThread permission, then call AbortInternal directly
+ // rather than delegating to the Abort() function below. We do this to ensure
+ // that only callers with ControlThread are allowed to change the AbortReason
+ // of the thread. We call AbortInternal directly to avoid demanding the same
+ // permission twice.
+ AbortInternal();
+ }
+#endif
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+#pragma warning disable 618
+ [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
+#pragma warning restore 618
+ public void Abort()
+ {
+ AbortInternal();
+ }
+
+ // Internal helper (since we can't place security demands on
+ // ecalls/fcalls).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void AbortInternal();
+
+#if !FEATURE_CORECLR
+ /*=========================================================================
+ ** Resets a thread abort.
+ ** Should be called by trusted code only
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread=true)]
+ public static void ResetAbort()
+ {
+ Thread thread = Thread.CurrentThread;
+ if ((thread.ThreadState & ThreadState.AbortRequested) == 0)
+ throw new ThreadStateException(Environment.GetResourceString("ThreadState_NoAbortRequested"));
+ thread.ResetAbortNative();
+ thread.ClearAbortReason();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void ResetAbortNative();
+
+ /*=========================================================================
+ ** Suspends the thread. If the thread is already suspended, this call has
+ ** no effect.
+ **
+ ** Exceptions: ThreadStateException if the thread has not been started or
+ ** it is dead.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [Obsolete("Thread.Suspend has been deprecated. Please use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources. http://go.microsoft.com/fwlink/?linkid=14202", false)][SecurityPermission(SecurityAction.Demand, ControlThread=true)]
+ [SecurityPermission(SecurityAction.Demand, ControlThread=true)]
+ public void Suspend() { SuspendInternal(); }
+
+ // Internal helper (since we can't place security demands on
+ // ecalls/fcalls).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void SuspendInternal();
+
+ /*=========================================================================
+ ** Resumes a thread that has been suspended.
+ **
+ ** Exceptions: ThreadStateException if the thread has not been started or
+ ** it is dead or it isn't in the suspended state.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [Obsolete("Thread.Resume has been deprecated. Please use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources. http://go.microsoft.com/fwlink/?linkid=14202", false)]
+ [SecurityPermission(SecurityAction.Demand, ControlThread=true)]
+ public void Resume() { ResumeInternal(); }
+
+ // Internal helper (since we can't place security demands on
+ // ecalls/fcalls).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void ResumeInternal();
+
+ /*=========================================================================
+ ** Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that
+ ** thread is not currently blocked in that manner, it will be interrupted
+ ** when it next begins to block.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SecurityPermission(SecurityAction.Demand, ControlThread=true)]
+ public void Interrupt() { InterruptInternal(); }
+
+ // Internal helper (since we can't place security demands on
+ // ecalls/fcalls).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void InterruptInternal();
+#endif
+
+ /*=========================================================================
+ ** Returns the priority of the thread.
+ **
+ ** Exceptions: ThreadStateException if the thread is dead.
+ =========================================================================*/
+
+ public ThreadPriority Priority {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get { return (ThreadPriority)GetPriorityNative(); }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(SelfAffectingThreading=true)]
+ set { SetPriorityNative((int)value); }
+ }
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern int GetPriorityNative();
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void SetPriorityNative(int priority);
+
+ /*=========================================================================
+ ** Returns true if the thread has been started and is not dead.
+ =========================================================================*/
+ public extern bool IsAlive {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ get;
+ }
+
+ /*=========================================================================
+ ** Returns true if the thread is a threadpool thread.
+ =========================================================================*/
+ public extern bool IsThreadPoolThread {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ get;
+ }
+
+ /*=========================================================================
+ ** Waits for the thread to die or for timeout milliseconds to elapse.
+ ** Returns true if the thread died, or false if the wait timed out. If
+ ** Timeout.Infinite is given as the parameter, no timeout will occur.
+ **
+ ** Exceptions: ArgumentException if timeout < 0.
+ ** ThreadInterruptedException if the thread is interrupted while waiting.
+ ** ThreadStateException if the thread has not been started yet.
+ =========================================================================*/
+ [System.Security.SecurityCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern bool JoinInternal(int millisecondsTimeout);
+
+ [System.Security.SecuritySafeCritical]
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ public void Join()
+ {
+ JoinInternal(Timeout.Infinite);
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ public bool Join(int millisecondsTimeout)
+ {
+ return JoinInternal(millisecondsTimeout);
+ }
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ public bool Join(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+
+ return Join((int)tm);
+ }
+
+ /*=========================================================================
+ ** Suspends the current thread for timeout milliseconds. If timeout == 0,
+ ** forces the thread to give up the remainer of its timeslice. If timeout
+ ** == Timeout.Infinite, no timeout will occur.
+ **
+ ** Exceptions: ArgumentException if timeout < 0.
+ ** ThreadInterruptedException if the thread is interrupted while sleeping.
+ =========================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void SleepInternal(int millisecondsTimeout);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void Sleep(int millisecondsTimeout)
+ {
+ SleepInternal(millisecondsTimeout);
+ // Ensure we don't return to app code when the pause is underway
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+ }
+
+ public static void Sleep(TimeSpan timeout)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1 || tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Sleep((int)tm);
+ }
+
+
+ /* wait for a length of time proportial to 'iterations'. Each iteration is should
+ only take a few machine instructions. Calling this API is preferable to coding
+ a explict busy loop because the hardware can be informed that it is busy waiting. */
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [HostProtection(Synchronization=true,ExternalThreading=true)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private static extern void SpinWaitInternal(int iterations);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true,ExternalThreading=true)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static void SpinWait(int iterations)
+ {
+ SpinWaitInternal(iterations);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private static extern bool YieldInternal();
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static bool Yield()
+ {
+ return YieldInternal();
+ }
+
+ public static Thread CurrentThread {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ get {
+ Contract.Ensures(Contract.Result<Thread>() != null);
+ return GetCurrentThreadNative();
+ }
+ }
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private static extern Thread GetCurrentThreadNative();
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void SetStartHelper(Delegate start, int maxStackSize)
+ {
+#if FEATURE_CORECLR
+ // We only support default stacks in CoreCLR
+ Contract.Assert(maxStackSize == 0);
+#else
+ // Only fully-trusted code is allowed to create "large" stacks. Partial-trust falls back to
+ // the default stack size.
+ ulong defaultStackSize = GetProcessDefaultStackSize();
+ if ((ulong)(uint)maxStackSize > defaultStackSize)
+ {
+ try
+ {
+ SecurityPermission.Demand(PermissionType.FullTrust);
+ }
+ catch (SecurityException)
+ {
+ maxStackSize = (int)Math.Min(defaultStackSize, (ulong)(uint)int.MaxValue);
+ }
+ }
+#endif
+
+ ThreadHelper threadStartCallBack = new ThreadHelper(start);
+ if(start is ThreadStart)
+ {
+ SetStart(new ThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
+ }
+ else
+ {
+ SetStart(new ParameterizedThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
+ }
+ }
+
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern ulong GetProcessDefaultStackSize();
+
+ /*=========================================================================
+ ** PRIVATE Sets the IThreadable interface for the thread. Assumes that
+ ** start != null.
+ =========================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void SetStart(Delegate start, int maxStackSize);
+
+ /*=========================================================================
+ ** Clean up the thread when it goes away.
+ =========================================================================*/
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ ~Thread()
+ {
+ // Delegate to the unmanaged portion.
+ InternalFinalize();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void InternalFinalize();
+
+#if FEATURE_COMINTEROP
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public extern void DisableComObjectEagerCleanup();
+#endif //FEATURE_COMINTEROP
+
+ /*=========================================================================
+ ** Return whether or not this thread is a background thread. Background
+ ** threads do not affect when the Execution Engine shuts down.
+ **
+ ** Exceptions: ThreadStateException if the thread is dead.
+ =========================================================================*/
+ public bool IsBackground {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get { return IsBackgroundNative(); }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(SelfAffectingThreading=true)]
+ set { SetBackgroundNative(value); }
+ }
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern bool IsBackgroundNative();
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void SetBackgroundNative(bool isBackground);
+
+
+ /*=========================================================================
+ ** Return the thread state as a consistent set of bits. This is more
+ ** general then IsAlive or IsBackground.
+ =========================================================================*/
+ public ThreadState ThreadState {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get { return (ThreadState)GetThreadStateNative(); }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern int GetThreadStateNative();
+
+#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
+ /*=========================================================================
+ ** An unstarted thread can be marked to indicate that it will host a
+ ** single-threaded or multi-threaded apartment.
+ **
+ ** Exceptions: ArgumentException if state is not a valid apartment state
+ ** (ApartmentSTA or ApartmentMTA).
+ =========================================================================*/
+ [Obsolete("The ApartmentState property has been deprecated. Use GetApartmentState, SetApartmentState or TrySetApartmentState instead.", false)]
+ public ApartmentState ApartmentState
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get
+ {
+ return (ApartmentState)GetApartmentStateNative();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true, SelfAffectingThreading=true)]
+ set
+ {
+ SetApartmentStateNative((int)value, true);
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public ApartmentState GetApartmentState()
+ {
+ return (ApartmentState)GetApartmentStateNative();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true, SelfAffectingThreading=true)]
+ public bool TrySetApartmentState(ApartmentState state)
+ {
+ return SetApartmentStateHelper(state, false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true, SelfAffectingThreading=true)]
+ public void SetApartmentState(ApartmentState state)
+ {
+ bool result = SetApartmentStateHelper(state, true);
+ if (!result)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ApartmentStateSwitchFailed"));
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private bool SetApartmentStateHelper(ApartmentState state, bool fireMDAOnMismatch)
+ {
+ ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state, fireMDAOnMismatch);
+
+ // Special case where we pass in Unknown and get back MTA.
+ // Once we CoUninitialize the thread, the OS will still
+ // report the thread as implicitly in the MTA if any
+ // other thread in the process is CoInitialized.
+ if ((state == System.Threading.ApartmentState.Unknown) && (retState == System.Threading.ApartmentState.MTA))
+ return true;
+
+ if (retState != state)
+ return false;
+
+ return true;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern int GetApartmentStateNative();
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern int SetApartmentStateNative(int state, bool fireMDAOnMismatch);
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void StartupSetApartmentStateInternal();
+#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+ /*=========================================================================
+ ** Allocates an un-named data slot. The slot is allocated on ALL the
+ ** threads.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static LocalDataStoreSlot AllocateDataSlot()
+ {
+ return LocalDataStoreManager.AllocateDataSlot();
+ }
+
+ /*=========================================================================
+ ** Allocates a named data slot. The slot is allocated on ALL the
+ ** threads. Named data slots are "public" and can be manipulated by
+ ** anyone.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static LocalDataStoreSlot AllocateNamedDataSlot(String name)
+ {
+ return LocalDataStoreManager.AllocateNamedDataSlot(name);
+ }
+
+ /*=========================================================================
+ ** Looks up a named data slot. If the name has not been used, a new slot is
+ ** allocated. Named data slots are "public" and can be manipulated by
+ ** anyone.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static LocalDataStoreSlot GetNamedDataSlot(String name)
+ {
+ return LocalDataStoreManager.GetNamedDataSlot(name);
+ }
+
+ /*=========================================================================
+ ** Frees a named data slot. The slot is allocated on ALL the
+ ** threads. Named data slots are "public" and can be manipulated by
+ ** anyone.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static void FreeNamedDataSlot(String name)
+ {
+ LocalDataStoreManager.FreeNamedDataSlot(name);
+ }
+
+ /*=========================================================================
+ ** Retrieves the value from the specified slot on the current thread, for that thread's current domain.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static Object GetData(LocalDataStoreSlot slot)
+ {
+ LocalDataStoreHolder dls = s_LocalDataStore;
+ if (dls == null)
+ {
+ // Make sure to validate the slot even if we take the quick path
+ LocalDataStoreManager.ValidateSlot(slot);
+ return null;
+ }
+
+ return dls.Store.GetData(slot);
+ }
+
+ /*=========================================================================
+ ** Sets the data in the specified slot on the currently running thread, for that thread's current domain.
+ =========================================================================*/
+ [HostProtection(SharedState=true, ExternalThreading=true)]
+ public static void SetData(LocalDataStoreSlot slot, Object data)
+ {
+ LocalDataStoreHolder dls = s_LocalDataStore;
+
+ // Create new DLS if one hasn't been created for this domain for this thread
+ if (dls == null) {
+ dls = LocalDataStoreManager.CreateLocalDataStore();
+ s_LocalDataStore = dls;
+ }
+
+ dls.Store.SetData(slot, data);
+ }
+
+
+ // #threadCultureInfo
+ //
+ // Background:
+ // In the desktop runtime, we allow a thread's cultures to travel with the thread
+ // across AppDomain boundaries. Furthermore we update the native thread with the
+ // culture of the managed thread. Because of security concerns and potential SxS
+ // effects, in Silverlight we are making the changes listed below.
+ //
+ // Silverlight Changes:
+ // - thread instance member cultures (CurrentCulture and CurrentUICulture)
+ // confined within AppDomains
+ // - changes to these properties don't affect the underlying native thread
+ //
+ // Ifdef:
+ // FEATURE_LEAK_CULTURE_INFO : CultureInfos can leak across AppDomains, not
+ // enabled in Silverlight
+ //
+ // Implementation notes:
+ // In Silverlight, culture members thread static (per Thread, per AppDomain).
+ //
+ // Quirks:
+ // An interesting side-effect of isolating cultures within an AppDomain is that we
+ // now need to special case resource lookup for mscorlib, which transitions to the
+ // default domain to lookup resources. See Environment.cs for more details.
+ //
+#if FEATURE_LEAK_CULTURE_INFO
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ static extern private bool nativeGetSafeCulture(Thread t, int appDomainId, bool isUI, ref CultureInfo safeCulture);
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+ // As the culture can be customized object then we cannot hold any
+ // reference to it before we check if it is safe because the app domain
+ // owning this customized culture may get unloaded while executing this
+ // code. To achieve that we have to do the check using nativeGetSafeCulture
+ // as the thread cannot get interrupted during the FCALL.
+ // If the culture is safe (not customized or created in current app domain)
+ // then the FCALL will return a reference to that culture otherwise the
+ // FCALL will return failure. In case of failure we'll return the default culture.
+ // If the app domain owning a customized culture that is set to the thread and this
+ // app domain get unloaded there is a code to clean up the culture from the thread
+ // using the code in AppDomain::ReleaseDomainStores.
+
+ public CultureInfo CurrentUICulture {
+ get {
+ Contract.Ensures(Contract.Result<CultureInfo>() != null);
+#if FEATURE_APPX && !FEATURE_COREFX_GLOBALIZATION
+ if(AppDomain.IsAppXModel()) {
+ return CultureInfo.GetCultureInfoForUserPreferredLanguageInAppX() ?? GetCurrentUICultureNoAppX();
+ }
+ else
+#endif
+ {
+ return GetCurrentUICultureNoAppX();
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(ExternalThreading=true)]
+ set {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+ Contract.EndContractBlock();
+
+ //If they're trying to use a Culture with a name that we can't use in resource lookup,
+ //don't even let them set it on the thread.
+ CultureInfo.VerifyCultureName(value, true);
+
+ // If you add more pre-conditions to this method, check to see if you also need to
+ // add them to CultureInfo.DefaultThreadCurrentUICulture.set.
+
+#if FEATURE_LEAK_CULTURE_INFO
+ if (nativeSetThreadUILocale(value.SortName) == false)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidResourceCultureName", value.Name));
+ }
+ value.StartCrossDomainTracking();
+#else
+ if (m_CurrentUICulture == null && m_CurrentCulture == null)
+ nativeInitCultureAccessors();
+#endif
+
+ if (!AppContextSwitches.NoAsyncCurrentCulture)
+ {
+ if (s_asyncLocalCurrentUICulture == null)
+ {
+ Interlocked.CompareExchange(ref s_asyncLocalCurrentUICulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentUICulture), null);
+ }
+
+ // this one will set m_CurrentUICulture too
+ s_asyncLocalCurrentUICulture.Value = value;
+ }
+ else
+ {
+ m_CurrentUICulture = value;
+ }
+ }
+ }
+
+#if FEATURE_LEAK_CULTURE_INFO
+ [System.Security.SecuritySafeCritical] // auto-generated
+#endif
+ internal CultureInfo GetCurrentUICultureNoAppX() {
+
+ Contract.Ensures(Contract.Result<CultureInfo>() != null);
+
+#if FEATURE_COREFX_GLOBALIZATION
+ return CultureInfo.CurrentUICulture;
+#else
+
+ // Fetch a local copy of m_CurrentUICulture to
+ // avoid race conditions that malicious user can introduce
+ if (m_CurrentUICulture == null) {
+ CultureInfo appDomainDefaultUICulture = CultureInfo.DefaultThreadCurrentUICulture;
+ return (appDomainDefaultUICulture != null ? appDomainDefaultUICulture : CultureInfo.UserDefaultUICulture);
+ }
+
+#if FEATURE_LEAK_CULTURE_INFO
+ CultureInfo culture = null;
+
+ if (!nativeGetSafeCulture(this, GetDomainID(), true, ref culture) || culture == null) {
+ return CultureInfo.UserDefaultUICulture;
+ }
+
+ return culture;
+#else
+ return m_CurrentUICulture;
+#endif
+#endif
+ }
+
+ // This returns the exposed context for a given context ID.
+#if FEATURE_LEAK_CULTURE_INFO
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ static extern private bool nativeSetThreadUILocale(String locale);
+#endif
+
+ // As the culture can be customized object then we cannot hold any
+ // reference to it before we check if it is safe because the app domain
+ // owning this customized culture may get unloaded while executing this
+ // code. To achieve that we have to do the check using nativeGetSafeCulture
+ // as the thread cannot get interrupted during the FCALL.
+ // If the culture is safe (not customized or created in current app domain)
+ // then the FCALL will return a reference to that culture otherwise the
+ // FCALL will return failure. In case of failure we'll return the default culture.
+ // If the app domain owning a customized culture that is set to the thread and this
+ // app domain get unloaded there is a code to clean up the culture from the thread
+ // using the code in AppDomain::ReleaseDomainStores.
+
+ public CultureInfo CurrentCulture {
+ get {
+ Contract.Ensures(Contract.Result<CultureInfo>() != null);
+
+#if FEATURE_APPX && !FEATURE_COREFX_GLOBALIZATION
+ if(AppDomain.IsAppXModel()) {
+ return CultureInfo.GetCultureInfoForUserPreferredLanguageInAppX() ?? GetCurrentCultureNoAppX();
+ }
+ else
+#endif
+ {
+ return GetCurrentCultureNoAppX();
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+#if FEATURE_LEAK_CULTURE_INFO
+ [SecurityPermission(SecurityAction.Demand, ControlThread = true)]
+#endif
+ set {
+ if (null==value) {
+ throw new ArgumentNullException("value");
+ }
+ Contract.EndContractBlock();
+
+ // If you add more pre-conditions to this method, check to see if you also need to
+ // add them to CultureInfo.DefaultThreadCurrentCulture.set.
+
+#if FEATURE_LEAK_CULTURE_INFO
+ //If we can't set the nativeThreadLocale, we'll just let it stay
+ //at whatever value it had before. This allows people who use
+ //just managed code not to be limited by the underlying OS.
+ CultureInfo.nativeSetThreadLocale(value.SortName);
+ value.StartCrossDomainTracking();
+#else
+ if (m_CurrentCulture == null && m_CurrentUICulture == null)
+ nativeInitCultureAccessors();
+#endif
+
+ if (!AppContextSwitches.NoAsyncCurrentCulture)
+ {
+ if (s_asyncLocalCurrentCulture == null)
+ {
+ Interlocked.CompareExchange(ref s_asyncLocalCurrentCulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentCulture), null);
+ }
+ // this one will set m_CurrentCulture too
+ s_asyncLocalCurrentCulture.Value = value;
+ }
+ else
+ {
+ m_CurrentCulture = value;
+ }
+ }
+ }
+
+#if FEATURE_LEAK_CULTURE_INFO
+ [System.Security.SecuritySafeCritical] // auto-generated
+#endif
+ private CultureInfo GetCurrentCultureNoAppX() {
+
+#if FEATURE_COREFX_GLOBALIZATION
+ return CultureInfo.CurrentCulture;
+#else
+ Contract.Ensures(Contract.Result<CultureInfo>() != null);
+
+ // Fetch a local copy of m_CurrentCulture to
+ // avoid race conditions that malicious user can introduce
+ if (m_CurrentCulture == null) {
+ CultureInfo appDomainDefaultCulture = CultureInfo.DefaultThreadCurrentCulture;
+ return (appDomainDefaultCulture != null ? appDomainDefaultCulture : CultureInfo.UserDefaultCulture);
+ }
+
+#if FEATURE_LEAK_CULTURE_INFO
+ CultureInfo culture = null;
+
+ if (!nativeGetSafeCulture(this, GetDomainID(), false, ref culture) || culture == null) {
+ return CultureInfo.UserDefaultCulture;
+ }
+
+ return culture;
+#else
+ return m_CurrentCulture;
+#endif
+#endif
+ }
+
+#if! FEATURE_LEAK_CULTURE_INFO
+ [System.Security.SecurityCritical] // auto-generated
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void nativeInitCultureAccessors();
+#endif
+
+ /*=============================================================*/
+
+ /*======================================================================
+ ** Current thread context is stored in a slot in the thread local store
+ ** CurrentContext gets the Context from the slot.
+ ======================================================================*/
+#if FEATURE_REMOTING
+ public static Context CurrentContext
+ {
+ [System.Security.SecurityCritical] // auto-generated_required
+ get
+ {
+ return CurrentThread.GetCurrentContextInternal();
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal Context GetCurrentContextInternal()
+ {
+ if (m_Context == null)
+ {
+ m_Context = Context.DefaultContext;
+ }
+ return m_Context;
+ }
+#endif
+
+
+#if FEATURE_IMPERSONATION
+ // Get and set thread's current principal (for role based security).
+ public static IPrincipal CurrentPrincipal
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get
+ {
+ lock (CurrentThread)
+ {
+ IPrincipal principal = (IPrincipal)
+ CallContext.Principal;
+ if (principal == null)
+ {
+ principal = GetDomain().GetThreadPrincipal();
+ CallContext.Principal = principal;
+ }
+ return principal;
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPrincipal)]
+ set
+ {
+ CallContext.Principal = value;
+ }
+ }
+
+ // Private routine called from unmanaged code to set an initial
+ // principal for a newly created thread.
+ [System.Security.SecurityCritical] // auto-generated
+ private void SetPrincipalInternal(IPrincipal principal)
+ {
+ GetMutableExecutionContext().LogicalCallContext.SecurityData.Principal = principal;
+ }
+#endif // FEATURE_IMPERSONATION
+
+#if FEATURE_REMOTING
+
+ // This returns the exposed context for a given context ID.
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern Context GetContextInternal(IntPtr id);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern Object InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args);
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal Object InternalCrossContextCallback(Context ctx, InternalCrossContextDelegate ftnToCall, Object[] args)
+ {
+ return InternalCrossContextCallback(ctx, ctx.InternalContextID, 0, ftnToCall, args);
+ }
+
+ // CompleteCrossContextCallback is called by the EE after transitioning to the requested context
+ private static Object CompleteCrossContextCallback(InternalCrossContextDelegate ftnToCall, Object[] args)
+ {
+ return ftnToCall(args);
+ }
+#endif // FEATURE_REMOTING
+
+ /*======================================================================
+ ** Returns the current domain in which current thread is running.
+ ======================================================================*/
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern AppDomain GetDomainInternal();
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern AppDomain GetFastDomainInternal();
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static AppDomain GetDomain()
+ {
+ Contract.Ensures(Contract.Result<AppDomain>() != null);
+
+
+ AppDomain ad;
+ ad = GetFastDomainInternal();
+ if (ad == null)
+ ad = GetDomainInternal();
+
+#if FEATURE_REMOTING
+ Contract.Assert(CurrentThread.m_Context == null || CurrentThread.m_Context.AppDomain == ad, "AppDomains on the managed & unmanaged threads should match");
+#endif
+ return ad;
+ }
+
+
+ /*
+ * This returns a unique id to identify an appdomain.
+ */
+ public static int GetDomainID()
+ {
+ return GetDomain().GetId();
+ }
+
+
+ // Retrieves the name of the thread.
+ //
+ public String Name {
+ get {
+ return m_Name;
+
+ }
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(ExternalThreading=true)]
+ set {
+ lock(this) {
+ if (m_Name != null)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WriteOnce"));
+ m_Name = value;
+
+ InformThreadNameChange(GetNativeHandle(), value, (value != null) ? value.Length : 0);
+ }
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void InformThreadNameChange(ThreadHandle t, String name, int len);
+
+ internal Object AbortReason {
+ [System.Security.SecurityCritical] // auto-generated
+ get {
+ object result = null;
+ try
+ {
+ result = GetAbortReason();
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ExceptionStateCrossAppDomain"), e);
+ }
+ return result;
+ }
+ [System.Security.SecurityCritical] // auto-generated
+ set { SetAbortReason(value); }
+ }
+
+#if !FEATURE_CORECLR
+ /*
+ * This marks the beginning of a critical code region.
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static extern void BeginCriticalRegion();
+
+ /*
+ * This marks the end of a critical code region.
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static extern void EndCriticalRegion();
+
+ /*
+ * This marks the beginning of a code region that requires thread affinity.
+ */
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static extern void BeginThreadAffinity();
+
+ /*
+ * This marks the end of a code region that requires thread affinity.
+ */
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static extern void EndThreadAffinity();
+#endif // !FEATURE_CORECLR
+
+ /*=========================================================================
+ ** Volatile Read & Write and MemoryBarrier methods.
+ ** Provides the ability to read and write values ensuring that the values
+ ** are read/written each time they are accessed.
+ =========================================================================*/
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static byte VolatileRead(ref byte address)
+ {
+ byte ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static short VolatileRead(ref short address)
+ {
+ short ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static int VolatileRead(ref int address)
+ {
+ int ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static long VolatileRead(ref long address)
+ {
+ long ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static sbyte VolatileRead(ref sbyte address)
+ {
+ sbyte ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static ushort VolatileRead(ref ushort address)
+ {
+ ushort ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static uint VolatileRead(ref uint address)
+ {
+ uint ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static IntPtr VolatileRead(ref IntPtr address)
+ {
+ IntPtr ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static UIntPtr VolatileRead(ref UIntPtr address)
+ {
+ UIntPtr ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static ulong VolatileRead(ref ulong address)
+ {
+ ulong ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static float VolatileRead(ref float address)
+ {
+ float ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static double VolatileRead(ref double address)
+ {
+ double ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static Object VolatileRead(ref Object address)
+ {
+ Object ret = address;
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ return ret;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref byte address, byte value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref short address, short value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref int address, int value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref long address, long value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref sbyte address, sbyte value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref ushort address, ushort value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref uint address, uint value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref IntPtr address, IntPtr value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref UIntPtr address, UIntPtr value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref ulong address, ulong value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref float address, float value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref double address, double value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
+ public static void VolatileWrite(ref Object address, Object value)
+ {
+ MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
+ address = value;
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern void MemoryBarrier();
+
+ private static LocalDataStoreMgr LocalDataStoreManager
+ {
+ get
+ {
+ if (s_LocalDataStoreMgr == null)
+ {
+ Interlocked.CompareExchange(ref s_LocalDataStoreMgr, new LocalDataStoreMgr(), null);
+ }
+
+ return s_LocalDataStoreMgr;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ void _Thread.GetTypeInfoCount(out uint pcTInfo)
+ {
+ throw new NotImplementedException();
+ }
+
+ void _Thread.GetTypeInfo(uint iTInfo, uint lcid, IntPtr ppTInfo)
+ {
+ throw new NotImplementedException();
+ }
+
+ void _Thread.GetIDsOfNames([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
+ {
+ throw new NotImplementedException();
+ }
+
+ void _Thread.Invoke(uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
+ {
+ throw new NotImplementedException();
+ }
+#endif
+
+ // Helper function to set the AbortReason for a thread abort.
+ // Checks that they're not alredy set, and then atomically updates
+ // the reason info (object + ADID).
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern void SetAbortReason(Object o);
+
+ // Helper function to retrieve the AbortReason from a thread
+ // abort. Will perform cross-AppDomain marshalling if the object
+ // lives in a different AppDomain from the requester.
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern Object GetAbortReason();
+
+ // Helper function to clear the AbortReason. Takes care of
+ // AppDomain related cleanup if required.
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern void ClearAbortReason();
+
+
+ } // End of class Thread
+
+ // declaring a local var of this enum type and passing it by ref into a function that needs to do a
+ // stack crawl will both prevent inlining of the calle and pass an ESP point to stack crawl to
+ // Declaring these in EH clauses is illegal; they must declared in the main method body
+ [Serializable]
+ internal enum StackCrawlMark
+ {
+ LookForMe = 0,
+ LookForMyCaller = 1,
+ LookForMyCallersCaller = 2,
+ LookForThread = 3
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadAbortException.cs b/src/mscorlib/src/System/Threading/ThreadAbortException.cs
new file mode 100644
index 0000000000..11c8744c72
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadAbortException.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: An exception class which is thrown into a thread to cause it to
+** abort. This is a special non-catchable exception and results in
+** the thread's death. This is thrown by the VM only and can NOT be
+** thrown by any user thread, and subclassing this is useless.
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ using System;
+ using System.Runtime.Serialization;
+ using System.Runtime.CompilerServices;
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public sealed class ThreadAbortException : SystemException
+ {
+ private ThreadAbortException()
+ : base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadAbort))
+ {
+ SetErrorCode(__HResults.COR_E_THREADABORTED);
+ }
+
+ //required for serialization
+ internal ThreadAbortException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public Object ExceptionState
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {return Thread.CurrentThread.AbortReason;}
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs
new file mode 100644
index 0000000000..0056611955
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: An exception class to indicate that the thread was interrupted
+** from a waiting state.
+**
+**
+=============================================================================*/
+namespace System.Threading {
+ using System.Threading;
+ using System;
+ using System.Runtime.Serialization;
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public class ThreadInterruptedException : SystemException {
+ public ThreadInterruptedException()
+ : base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted)) {
+ SetErrorCode(__HResults.COR_E_THREADINTERRUPTED);
+ }
+
+ public ThreadInterruptedException(String message)
+ : base(message) {
+ SetErrorCode(__HResults.COR_E_THREADINTERRUPTED);
+ }
+
+ public ThreadInterruptedException(String message, Exception innerException)
+ : base(message, innerException) {
+ SetErrorCode(__HResults.COR_E_THREADINTERRUPTED);
+ }
+
+ protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadLocal.cs b/src/mscorlib/src/System/Threading/ThreadLocal.cs
new file mode 100644
index 0000000000..b4cf12ab7c
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadLocal.cs
@@ -0,0 +1,815 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A class that provides a simple, lightweight implementation of thread-local lazy-initialization, where a value is initialized once per accessing
+// thread; this provides an alternative to using a ThreadStatic static variable and having
+// to check the variable prior to every access to see if it's been initialized.
+//
+//
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Provides thread-local storage of data.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of data stored per-thread.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// With the exception of <see cref="Dispose()"/>, all public and protected members of
+ /// <see cref="ThreadLocal{T}"/> are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </para>
+ /// </remarks>
+ [DebuggerTypeProxy(typeof(SystemThreading_ThreadLocalDebugView<>))]
+ [DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}, Count={ValuesCountForDebugDisplay}")]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class ThreadLocal<T> : IDisposable
+ {
+
+ // a delegate that returns the created value, if null the created value will be default(T)
+ private Func<T> m_valueFactory;
+
+ //
+ // ts_slotArray is a table of thread-local values for all ThreadLocal<T> instances
+ //
+ // So, when a thread reads ts_slotArray, it gets back an array of *all* ThreadLocal<T> values for this thread and this T.
+ // The slot relevant to this particular ThreadLocal<T> instance is determined by the m_idComplement instance field stored in
+ // the ThreadLocal<T> instance.
+ //
+ [ThreadStatic]
+ static LinkedSlotVolatile[] ts_slotArray;
+
+ [ThreadStatic]
+ static FinalizationHelper ts_finalizationHelper;
+
+ // Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish
+ // between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or
+ // possibly due to a memory model issue in user code.
+ private int m_idComplement;
+
+ // This field is set to true when the constructor completes. That is helpful for recognizing whether a constructor
+ // threw an exception - either due to invalid argument or due to a thread abort. Finally, the field is set to false
+ // when the instance is disposed.
+ private volatile bool m_initialized;
+
+ // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock.
+ private static IdManager s_idManager = new IdManager();
+
+ // A linked list of all values associated with this ThreadLocal<T> instance.
+ // We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field.
+ private LinkedSlot m_linkedSlot = new LinkedSlot(null);
+
+ // Whether the Values property is supported
+ private bool m_trackAllValues;
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
+ /// </summary>
+ public ThreadLocal()
+ {
+ Initialize(null, false);
+ }
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
+ /// </summary>
+ /// <param name="trackAllValues">Whether to track all values set on the instance and expose them through the Values property.</param>
+ public ThreadLocal(bool trackAllValues)
+ {
+ Initialize(null, trackAllValues);
+ }
+
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
+ /// specified <paramref name="valueFactory"/> function.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
+ /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ public ThreadLocal(Func<T> valueFactory)
+ {
+ if (valueFactory == null)
+ throw new ArgumentNullException("valueFactory");
+
+ Initialize(valueFactory, false);
+ }
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
+ /// specified <paramref name="valueFactory"/> function.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
+ /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
+ /// </param>
+ /// <param name="trackAllValues">Whether to track all values set on the instance and expose them via the Values property.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ public ThreadLocal(Func<T> valueFactory, bool trackAllValues)
+ {
+ if (valueFactory == null)
+ throw new ArgumentNullException("valueFactory");
+
+ Initialize(valueFactory, trackAllValues);
+ }
+
+ private void Initialize(Func<T> valueFactory, bool trackAllValues)
+ {
+ m_valueFactory = valueFactory;
+ m_trackAllValues = trackAllValues;
+
+ // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set m_initialized
+ // in a finally block, to avoid a thread abort in between the two statements.
+ try { }
+ finally
+ {
+ m_idComplement = ~s_idManager.GetId();
+
+ // As the last step, mark the instance as fully initialized. (Otherwise, if m_initialized=false, we know that an exception
+ // occurred in the constructor.)
+ m_initialized = true;
+ }
+ }
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ ~ThreadLocal()
+ {
+ // finalizer to return the type combination index to the pool
+ Dispose(false);
+ }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
+ /// </remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ /// <param name="disposing">
+ /// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>.
+ /// </param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ int id;
+
+ lock (s_idManager)
+ {
+ id = ~m_idComplement;
+ m_idComplement = 0;
+
+ if (id < 0 || !m_initialized)
+ {
+ Contract.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized");
+
+ // Handle double Dispose calls or disposal of an instance whose constructor threw an exception.
+ return;
+ }
+ m_initialized = false;
+
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray;
+
+ if (slotArray == null)
+ {
+ // The thread that owns this slotArray has already finished.
+ continue;
+ }
+
+ // Remove the reference from the LinkedSlot to the slot table.
+ linkedSlot.SlotArray = null;
+
+ // And clear the references from the slot table to the linked slot and the value so that
+ // both can get garbage collected.
+ slotArray[id].Value.Value = default(T);
+ slotArray[id].Value = null;
+ }
+ }
+ m_linkedSlot = null;
+ s_idManager.ReturnId(id);
+ }
+
+ #endregion
+
+ /// <summary>Creates and returns a string representation of this instance for the current thread.</summary>
+ /// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see cref="Value"/>.</returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <see cref="Value"/> for the current thread is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The initialization function referenced <see cref="Value"/> in an improper manner.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling this method forces initialization for the current thread, as is the
+ /// case with accessing <see cref="Value"/> directly.
+ /// </remarks>
+ public override string ToString()
+ {
+ return Value.ToString();
+ }
+
+ /// <summary>
+ /// Gets or sets the value of this instance for the current thread.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The initialization function referenced <see cref="Value"/> in an improper manner.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ /// <remarks>
+ /// If this instance was not previously initialized for the current thread,
+ /// accessing <see cref="Value"/> will attempt to initialize it. If an initialization function was
+ /// supplied during the construction, that initialization will happen by invoking the function
+ /// to retrieve the initial value for <see cref="Value"/>. Otherwise, the default value of
+ /// <typeparamref name="T"/> will be used.
+ /// </remarks>
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ public T Value
+ {
+ get
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ LinkedSlot slot;
+ int id = ~m_idComplement;
+
+ //
+ // Attempt to get the value using the fast path
+ //
+ if (slotArray != null // Has the slot array been initialized?
+ && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
+ && id < slotArray.Length // Is the table large enough?
+ && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
+ && m_initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
+ )
+ {
+ // We verified that the instance has not been disposed *after* we got a reference to the slot.
+ // This guarantees that we have a reference to the right slot.
+ //
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // will not be reordered before the read of slotArray[id].
+ return slot.Value;
+ }
+
+ return GetValueSlow();
+ }
+ set
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ LinkedSlot slot;
+ int id = ~m_idComplement;
+
+ //
+ // Attempt to set the value using the fast path
+ //
+ if (slotArray != null // Has the slot array been initialized?
+ && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
+ && id < slotArray.Length // Is the table large enough?
+ && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
+ && m_initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
+ )
+ {
+ // We verified that the instance has not been disposed *after* we got a reference to the slot.
+ // This guarantees that we have a reference to the right slot.
+ //
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // will not be reordered before the read of slotArray[id].
+ slot.Value = value;
+ }
+ else
+ {
+ SetValueSlow(value, slotArray);
+ }
+ }
+ }
+
+ private T GetValueSlow()
+ {
+ // If the object has been disposed, the id will be -1.
+ int id = ~m_idComplement;
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ Debugger.NotifyOfCrossThreadDependency();
+
+ // Determine the initial value
+ T value;
+ if (m_valueFactory == null)
+ {
+ value = default(T);
+ }
+ else
+ {
+ value = m_valueFactory();
+
+ if (IsValueCreated)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_Value_RecursiveCallsToValue"));
+ }
+ }
+
+ // Since the value has been previously uninitialized, we also need to set it (according to the ThreadLocal semantics).
+ Value = value;
+ return value;
+ }
+
+ private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray)
+ {
+ int id = ~m_idComplement;
+
+ // If the object has been disposed, id will be -1.
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ // If a slot array has not been created on this thread yet, create it.
+ if (slotArray == null)
+ {
+ slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)];
+ ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues);
+ ts_slotArray = slotArray;
+ }
+
+ // If the slot array is not big enough to hold this ID, increase the table size.
+ if (id >= slotArray.Length)
+ {
+ GrowTable(ref slotArray, id + 1);
+ ts_finalizationHelper.SlotArray = slotArray;
+ ts_slotArray = slotArray;
+ }
+
+ // If we are using the slot in this table for the first time, create a new LinkedSlot and add it into
+ // the linked list for this ThreadLocal instance.
+ if (slotArray[id].Value == null)
+ {
+ CreateLinkedSlot(slotArray, id, value);
+ }
+ else
+ {
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // that follows will not be reordered before the read of slotArray[id].
+ LinkedSlot slot = slotArray[id].Value;
+
+ // It is important to verify that the ThreadLocal instance has not been disposed. The check must come
+ // after capturing slotArray[id], but before assigning the value into the slot. This ensures that
+ // if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was
+ // created, we definitely won't assign the value into the wrong instance.
+
+ if (!m_initialized)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ slot.Value = value;
+ }
+ }
+
+ /// <summary>
+ /// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance.
+ /// </summary>
+ private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value)
+ {
+ // Create a LinkedSlot
+ var linkedSlot = new LinkedSlot(slotArray);
+
+ // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array
+ lock (s_idManager)
+ {
+ // Check that the instance has not been disposed. It is important to check this under a lock, since
+ // Dispose also executes under a lock.
+ if (!m_initialized)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ LinkedSlot firstRealNode = m_linkedSlot.Next;
+
+ //
+ // Insert linkedSlot between nodes m_linkedSlot and firstRealNode.
+ // (m_linkedSlot is the dummy head node that should always be in the front.)
+ //
+ linkedSlot.Next = firstRealNode;
+ linkedSlot.Previous = m_linkedSlot;
+ linkedSlot.Value = value;
+
+ if (firstRealNode != null)
+ {
+ firstRealNode.Previous = linkedSlot;
+ }
+ m_linkedSlot.Next = linkedSlot;
+
+ // Assigning the slot under a lock prevents a race condition with Dispose (dispose also acquires the lock).
+ // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created
+ // with the same ID, and the write would go to the wrong instance.
+ slotArray[id].Value = linkedSlot;
+ }
+ }
+
+ /// <summary>
+ /// Gets a list for all of the values currently stored by all of the threads that have accessed this instance.
+ /// </summary>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ public IList<T> Values
+ {
+ get
+ {
+ if (!m_trackAllValues)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_ValuesNotAvailable"));
+ }
+
+ var list = GetValuesAsList(); // returns null if disposed
+ if (list == null) throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ return list;
+ }
+ }
+
+ /// <summary>Gets all of the threads' values in a list.</summary>
+ private List<T> GetValuesAsList()
+ {
+ List<T> valueList = new List<T>();
+ int id = ~m_idComplement;
+ if (id == -1)
+ {
+ return null;
+ }
+
+ // Walk over the linked list of slots and gather the values associated with this ThreadLocal instance.
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ // We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot
+ // objects will never be assigned to another ThreadLocal instance.
+ valueList.Add(linkedSlot.Value);
+ }
+
+ return valueList;
+ }
+
+ /// <summary>Gets the number of threads that have data in this instance.</summary>
+ private int ValuesCountForDebugDisplay
+ {
+ get
+ {
+ int count = 0;
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ count++;
+ }
+ return count;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether <see cref="Value"/> is initialized on the current thread.
+ /// </summary>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ public bool IsValueCreated
+ {
+ get
+ {
+ int id = ~m_idComplement;
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ return slotArray != null && id < slotArray.Length && slotArray[id].Value != null;
+ }
+ }
+
+
+ /// <summary>Gets the value of the ThreadLocal&lt;T&gt; for debugging display purposes. It takes care of getting
+ /// the value for the current thread in the ThreadLocal mode.</summary>
+ internal T ValueForDebugDisplay
+ {
+ get
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ int id = ~m_idComplement;
+
+ LinkedSlot slot;
+ if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !m_initialized)
+ return default(T);
+ return slot.Value;
+ }
+ }
+
+ /// <summary>Gets the values of all threads that accessed the ThreadLocal&lt;T&gt;.</summary>
+ internal List<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed
+ {
+ get { return GetValuesAsList(); }
+ }
+
+ /// <summary>
+ /// Resizes a table to a certain length (or larger).
+ /// </summary>
+ private void GrowTable(ref LinkedSlotVolatile[] table, int minLength)
+ {
+ Contract.Assert(table.Length < minLength);
+
+ // Determine the size of the new table and allocate it.
+ int newLen = GetNewTableSize(minLength);
+ LinkedSlotVolatile[] newTable = new LinkedSlotVolatile[newLen];
+
+ //
+ // The lock is necessary to avoid a race with ThreadLocal.Dispose. GrowTable has to point all
+ // LinkedSlot instances referenced in the old table to reference the new table. Without locking,
+ // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while
+ // the value continues to be referenced from the new (larger) array.
+ //
+ lock (s_idManager)
+ {
+ for (int i = 0; i < table.Length; i++)
+ {
+ LinkedSlot linkedSlot = table[i].Value;
+ if (linkedSlot != null && linkedSlot.SlotArray != null)
+ {
+ linkedSlot.SlotArray = newTable;
+ newTable[i] = table[i];
+ }
+ }
+ }
+
+ table = newTable;
+ }
+
+ /// <summary>
+ /// Chooses the next larger table size
+ /// </summary>
+ private static int GetNewTableSize(int minSize)
+ {
+ if ((uint)minSize > Array.MaxArrayLength)
+ {
+ // Intentionally return a value that will result in an OutOfMemoryException
+ return int.MaxValue;
+ }
+ Contract.Assert(minSize > 0);
+
+ //
+ // Round up the size to the next power of 2
+ //
+ // The algorithm takes three steps:
+ // input -> subtract one -> propagate 1-bits to the right -> add one
+ //
+ // Let's take a look at the 3 steps in both interesting cases: where the input
+ // is (Example 1) and isn't (Example 2) a power of 2.
+ //
+ // Example 1: 100000 -> 011111 -> 011111 -> 100000
+ // Example 2: 011010 -> 011001 -> 011111 -> 100000
+ //
+ int newSize = minSize;
+
+ // Step 1: Decrement
+ newSize--;
+
+ // Step 2: Propagate 1-bits to the right.
+ newSize |= newSize >> 1;
+ newSize |= newSize >> 2;
+ newSize |= newSize >> 4;
+ newSize |= newSize >> 8;
+ newSize |= newSize >> 16;
+
+ // Step 3: Increment
+ newSize++;
+
+ // Don't set newSize to more than Array.MaxArrayLength
+ if ((uint)newSize > Array.MaxArrayLength)
+ {
+ newSize = Array.MaxArrayLength;
+ }
+
+ return newSize;
+ }
+
+ /// <summary>
+ /// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics
+ /// on array accesses.
+ /// </summary>
+ private struct LinkedSlotVolatile
+ {
+ internal volatile LinkedSlot Value;
+ }
+
+ /// <summary>
+ /// A node in the doubly-linked list stored in the ThreadLocal instance.
+ ///
+ /// The value is stored in one of two places:
+ ///
+ /// 1. If SlotArray is not null, the value is in SlotArray.Table[id]
+ /// 2. If SlotArray is null, the value is in FinalValue.
+ /// </summary>
+ private sealed class LinkedSlot
+ {
+ internal LinkedSlot(LinkedSlotVolatile[] slotArray)
+ {
+ SlotArray = slotArray;
+ }
+
+ // The next LinkedSlot for this ThreadLocal<> instance
+ internal volatile LinkedSlot Next;
+
+ // The previous LinkedSlot for this ThreadLocal<> instance
+ internal volatile LinkedSlot Previous;
+
+ // The SlotArray that stores this LinkedSlot at SlotArray.Table[id].
+ internal volatile LinkedSlotVolatile[] SlotArray;
+
+ // The value for this slot.
+ internal T Value;
+ }
+
+ /// <summary>
+ /// A manager class that assigns IDs to ThreadLocal instances
+ /// </summary>
+ private class IdManager
+ {
+ // The next ID to try
+ private int m_nextIdToTry = 0;
+
+ // Stores whether each ID is free or not. Additionally, the object is also used as a lock for the IdManager.
+ private List<bool> m_freeIds = new List<bool>();
+
+ internal int GetId()
+ {
+ lock (m_freeIds)
+ {
+ int availableId = m_nextIdToTry;
+ while (availableId < m_freeIds.Count)
+ {
+ if (m_freeIds[availableId]) { break; }
+ availableId++;
+ }
+
+ if (availableId == m_freeIds.Count)
+ {
+ m_freeIds.Add(false);
+ }
+ else
+ {
+ m_freeIds[availableId] = false;
+ }
+
+ m_nextIdToTry = availableId + 1;
+
+ return availableId;
+ }
+ }
+
+ // Return an ID to the pool
+ internal void ReturnId(int id)
+ {
+ lock (m_freeIds)
+ {
+ m_freeIds[id] = true;
+ if (id < m_nextIdToTry) m_nextIdToTry = id;
+ }
+ }
+ }
+
+ /// <summary>
+ /// A class that facilitates ThreadLocal cleanup after a thread exits.
+ ///
+ /// After a thread with an associated thread-local table has exited, the FinalizationHelper
+ /// is reponsible for removing back-references to the table. Since an instance of FinalizationHelper
+ /// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once
+ /// the thread has exited.
+ ///
+ /// The FinalizationHelper then locates all LinkedSlot instances with back-references to the table
+ /// (all those LinkedSlot instances can be found by following references from the table slots) and
+ /// releases the table so that it can get GC'd.
+ /// </summary>
+ private class FinalizationHelper
+ {
+ internal LinkedSlotVolatile[] SlotArray;
+ private bool m_trackAllValues;
+
+ internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues)
+ {
+ SlotArray = slotArray;
+ m_trackAllValues = trackAllValues;
+ }
+
+ ~FinalizationHelper()
+ {
+ LinkedSlotVolatile[] slotArray = SlotArray;
+ Contract.Assert(slotArray != null);
+
+ for (int i = 0; i < slotArray.Length; i++)
+ {
+ LinkedSlot linkedSlot = slotArray[i].Value;
+ if (linkedSlot == null)
+ {
+ // This slot in the table is empty
+ continue;
+ }
+
+ if (m_trackAllValues)
+ {
+ // Set the SlotArray field to null to release the slot array.
+ linkedSlot.SlotArray = null;
+ }
+ else
+ {
+ // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to
+ // the table will be have been removed, and so the table can get GC'd.
+ lock (s_idManager)
+ {
+ if (linkedSlot.Next != null)
+ {
+ linkedSlot.Next.Previous = linkedSlot.Previous;
+ }
+
+ // Since the list uses a dummy head node, the Previous reference should never be null.
+ Contract.Assert(linkedSlot.Previous != null);
+ linkedSlot.Previous.Next = linkedSlot.Next;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>A debugger view of the ThreadLocal&lt;T&gt; to surface additional debugging properties and
+ /// to ensure that the ThreadLocal&lt;T&gt; does not become initialized if it was not already.</summary>
+ internal sealed class SystemThreading_ThreadLocalDebugView<T>
+ {
+ //The ThreadLocal object being viewed.
+ private readonly ThreadLocal<T> m_tlocal;
+
+ /// <summary>Constructs a new debugger view object for the provided ThreadLocal object.</summary>
+ /// <param name="tlocal">A ThreadLocal object to browse in the debugger.</param>
+ public SystemThreading_ThreadLocalDebugView(ThreadLocal<T> tlocal)
+ {
+ m_tlocal = tlocal;
+ }
+
+ /// <summary>Returns whether the ThreadLocal object is initialized or not.</summary>
+ public bool IsValueCreated
+ {
+ get { return m_tlocal.IsValueCreated; }
+ }
+
+ /// <summary>Returns the value of the ThreadLocal object.</summary>
+ public T Value
+ {
+ get
+ {
+ return m_tlocal.ValueForDebugDisplay;
+ }
+ }
+
+ /// <summary>Return all values for all threads that have accessed this instance.</summary>
+ public List<T> Values
+ {
+ get
+ {
+ return m_tlocal.ValuesForDebugDisplay;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadPool.cs b/src/mscorlib/src/System/Threading/ThreadPool.cs
new file mode 100644
index 0000000000..09fe93c682
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadPool.cs
@@ -0,0 +1,1954 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Class for creating and managing a threadpool
+**
+**
+=============================================================================*/
+
+#pragma warning disable 0420
+
+/*
+ * Below you'll notice two sets of APIs that are separated by the
+ * use of 'Unsafe' in their names. The unsafe versions are called
+ * that because they do not propagate the calling stack onto the
+ * worker thread. This allows code to lose the calling stack and
+ * thereby elevate its security privileges. Note that this operation
+ * is much akin to the combined ability to control security policy
+ * and control security evidence. With these privileges, a person
+ * can gain the right to load assemblies that are fully trusted which
+ * then assert full trust and can call any code they want regardless
+ * of the previous stack information.
+ */
+
+namespace System.Threading
+{
+ using System.Security;
+ using System.Runtime.Remoting;
+ using System.Security.Permissions;
+ using System;
+ using Microsoft.Win32;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.InteropServices;
+ using System.Runtime.Versioning;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Tracing;
+
+ internal static class ThreadPoolGlobals
+ {
+ //Per-appDomain quantum (in ms) for which the thread keeps processing
+ //requests in the current domain.
+ public static uint tpQuantum = 30U;
+
+ public static int processorCount = Environment.ProcessorCount;
+
+ public static bool tpHosted = ThreadPool.IsThreadPoolHosted();
+
+ public static volatile bool vmTpInitialized;
+ public static bool enableWorkerTracking;
+
+ [SecurityCritical]
+ public static ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
+
+ [System.Security.SecuritySafeCritical] // static constructors should be safe to call
+ static ThreadPoolGlobals()
+ {
+ }
+ }
+
+ internal sealed class ThreadPoolWorkQueue
+ {
+ // Simple sparsely populated array to allow lock-free reading.
+ internal class SparseArray<T> where T : class
+ {
+ private volatile T[] m_array;
+
+ internal SparseArray(int initialSize)
+ {
+ m_array = new T[initialSize];
+ }
+
+ internal T[] Current
+ {
+ get { return m_array; }
+ }
+
+ internal int Add(T e)
+ {
+ while (true)
+ {
+ T[] array = m_array;
+ lock (array)
+ {
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (array[i] == null)
+ {
+ Volatile.Write(ref array[i], e);
+ return i;
+ }
+ else if (i == array.Length - 1)
+ {
+ // Must resize. If there was a race condition, we start over again.
+ if (array != m_array)
+ continue;
+
+ T[] newArray = new T[array.Length * 2];
+ Array.Copy(array, newArray, i + 1);
+ newArray[i + 1] = e;
+ m_array = newArray;
+ return i + 1;
+ }
+ }
+ }
+ }
+ }
+
+ internal void Remove(T e)
+ {
+ T[] array = m_array;
+ lock (array)
+ {
+ for (int i = 0; i < m_array.Length; i++)
+ {
+ if (m_array[i] == e)
+ {
+ Volatile.Write(ref m_array[i], null);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ internal class WorkStealingQueue
+ {
+ private const int INITIAL_SIZE = 32;
+ internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE];
+ private volatile int m_mask = INITIAL_SIZE - 1;
+
+#if DEBUG
+ // in debug builds, start at the end so we exercise the index reset logic.
+ private const int START_INDEX = int.MaxValue;
+#else
+ private const int START_INDEX = 0;
+#endif
+
+ private volatile int m_headIndex = START_INDEX;
+ private volatile int m_tailIndex = START_INDEX;
+
+ private SpinLock m_foreignLock = new SpinLock(false);
+
+ public void LocalPush(IThreadPoolWorkItem obj)
+ {
+ int tail = m_tailIndex;
+
+ // We're going to increment the tail; if we'll overflow, then we need to reset our counts
+ if (tail == int.MaxValue)
+ {
+ bool lockTaken = false;
+ try
+ {
+ m_foreignLock.Enter(ref lockTaken);
+
+ if (m_tailIndex == int.MaxValue)
+ {
+ //
+ // Rather than resetting to zero, we'll just mask off the bits we don't care about.
+ // This way we don't need to rearrange the items already in the queue; they'll be found
+ // correctly exactly where they are. One subtlety here is that we need to make sure that
+ // if head is currently < tail, it remains that way. This happens to just fall out from
+ // the bit-masking, because we only do this if tail == int.MaxValue, meaning that all
+ // bits are set, so all of the bits we're keeping will also be set. Thus it's impossible
+ // for the head to end up > than the tail, since you can't set any more bits than all of
+ // them.
+ //
+ m_headIndex = m_headIndex & m_mask;
+ m_tailIndex = tail = m_tailIndex & m_mask;
+ Contract.Assert(m_headIndex <= m_tailIndex);
+ }
+ }
+ finally
+ {
+ if (lockTaken)
+ m_foreignLock.Exit(true);
+ }
+ }
+
+ // When there are at least 2 elements' worth of space, we can take the fast path.
+ if (tail < m_headIndex + m_mask)
+ {
+ Volatile.Write(ref m_array[tail & m_mask], obj);
+ m_tailIndex = tail + 1;
+ }
+ else
+ {
+ // We need to contend with foreign pops, so we lock.
+ bool lockTaken = false;
+ try
+ {
+ m_foreignLock.Enter(ref lockTaken);
+
+ int head = m_headIndex;
+ int count = m_tailIndex - m_headIndex;
+
+ // If there is still space (one left), just add the element.
+ if (count >= m_mask)
+ {
+ // We're full; expand the queue by doubling its size.
+ IThreadPoolWorkItem[] newArray = new IThreadPoolWorkItem[m_array.Length << 1];
+ for (int i = 0; i < m_array.Length; i++)
+ newArray[i] = m_array[(i + head) & m_mask];
+
+ // Reset the field values, incl. the mask.
+ m_array = newArray;
+ m_headIndex = 0;
+ m_tailIndex = tail = count;
+ m_mask = (m_mask << 1) | 1;
+ }
+
+ Volatile.Write(ref m_array[tail & m_mask], obj);
+ m_tailIndex = tail + 1;
+ }
+ finally
+ {
+ if (lockTaken)
+ m_foreignLock.Exit(false);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public bool LocalFindAndPop(IThreadPoolWorkItem obj)
+ {
+ // Fast path: check the tail. If equal, we can skip the lock.
+ if (m_array[(m_tailIndex - 1) & m_mask] == obj)
+ {
+ IThreadPoolWorkItem unused;
+ if (LocalPop(out unused))
+ {
+ Contract.Assert(unused == obj);
+ return true;
+ }
+ return false;
+ }
+
+ // Else, do an O(N) search for the work item. The theory of work stealing and our
+ // inlining logic is that most waits will happen on recently queued work. And
+ // since recently queued work will be close to the tail end (which is where we
+ // begin our search), we will likely find it quickly. In the worst case, we
+ // will traverse the whole local queue; this is typically not going to be a
+ // problem (although degenerate cases are clearly an issue) because local work
+ // queues tend to be somewhat shallow in length, and because if we fail to find
+ // the work item, we are about to block anyway (which is very expensive).
+ for (int i = m_tailIndex - 2; i >= m_headIndex; i--)
+ {
+ if (m_array[i & m_mask] == obj)
+ {
+ // If we found the element, block out steals to avoid interference.
+ bool lockTaken = false;
+ try
+ {
+ m_foreignLock.Enter(ref lockTaken);
+
+ // If we encountered a race condition, bail.
+ if (m_array[i & m_mask] == null)
+ return false;
+
+ // Otherwise, null out the element.
+ Volatile.Write(ref m_array[i & m_mask], null);
+
+ // And then check to see if we can fix up the indexes (if we're at
+ // the edge). If we can't, we just leave nulls in the array and they'll
+ // get filtered out eventually (but may lead to superflous resizing).
+ if (i == m_tailIndex)
+ m_tailIndex -= 1;
+ else if (i == m_headIndex)
+ m_headIndex += 1;
+
+ return true;
+ }
+ finally
+ {
+ if (lockTaken)
+ m_foreignLock.Exit(false);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public bool LocalPop(out IThreadPoolWorkItem obj)
+ {
+ while (true)
+ {
+ // Decrement the tail using a fence to ensure subsequent read doesn't come before.
+ int tail = m_tailIndex;
+ if (m_headIndex >= tail)
+ {
+ obj = null;
+ return false;
+ }
+
+ tail -= 1;
+ Interlocked.Exchange(ref m_tailIndex, tail);
+
+ // If there is no interaction with a take, we can head down the fast path.
+ if (m_headIndex <= tail)
+ {
+ int idx = tail & m_mask;
+ obj = Volatile.Read(ref m_array[idx]);
+
+ // Check for nulls in the array.
+ if (obj == null) continue;
+
+ m_array[idx] = null;
+ return true;
+ }
+ else
+ {
+ // Interaction with takes: 0 or 1 elements left.
+ bool lockTaken = false;
+ try
+ {
+ m_foreignLock.Enter(ref lockTaken);
+
+ if (m_headIndex <= tail)
+ {
+ // Element still available. Take it.
+ int idx = tail & m_mask;
+ obj = Volatile.Read(ref m_array[idx]);
+
+ // Check for nulls in the array.
+ if (obj == null) continue;
+
+ m_array[idx] = null;
+ return true;
+ }
+ else
+ {
+ // If we encountered a race condition and element was stolen, restore the tail.
+ m_tailIndex = tail + 1;
+ obj = null;
+ return false;
+ }
+ }
+ finally
+ {
+ if (lockTaken)
+ m_foreignLock.Exit(false);
+ }
+ }
+ }
+ }
+
+ public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal)
+ {
+ return TrySteal(out obj, ref missedSteal, 0); // no blocking by default.
+ }
+
+ private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout)
+ {
+ obj = null;
+
+ while (true)
+ {
+ if (m_headIndex >= m_tailIndex)
+ return false;
+
+ bool taken = false;
+ try
+ {
+ m_foreignLock.TryEnter(millisecondsTimeout, ref taken);
+ if (taken)
+ {
+ // Increment head, and ensure read of tail doesn't move before it (fence).
+ int head = m_headIndex;
+ Interlocked.Exchange(ref m_headIndex, head + 1);
+
+ if (head < m_tailIndex)
+ {
+ int idx = head & m_mask;
+ obj = Volatile.Read(ref m_array[idx]);
+
+ // Check for nulls in the array.
+ if (obj == null) continue;
+
+ m_array[idx] = null;
+ return true;
+ }
+ else
+ {
+ // Failed, restore head.
+ m_headIndex = head;
+ obj = null;
+ missedSteal = true;
+ }
+ }
+ else
+ {
+ missedSteal = true;
+ }
+ }
+ finally
+ {
+ if (taken)
+ m_foreignLock.Exit(false);
+ }
+
+ return false;
+ }
+ }
+ }
+
+ internal class QueueSegment
+ {
+ // Holds a segment of the queue. Enqueues/Dequeues start at element 0, and work their way up.
+ internal readonly IThreadPoolWorkItem[] nodes;
+ private const int QueueSegmentLength = 256;
+
+ // Holds the indexes of the lowest and highest valid elements of the nodes array.
+ // The low index is in the lower 16 bits, high index is in the upper 16 bits.
+ // Use GetIndexes and CompareExchangeIndexes to manipulate this.
+ private volatile int indexes;
+
+ // The next segment in the queue.
+ public volatile QueueSegment Next;
+
+
+ const int SixteenBits = 0xffff;
+
+ void GetIndexes(out int upper, out int lower)
+ {
+ int i = indexes;
+ upper = (i >> 16) & SixteenBits;
+ lower = i & SixteenBits;
+
+ Contract.Assert(upper >= lower);
+ Contract.Assert(upper <= nodes.Length);
+ Contract.Assert(lower <= nodes.Length);
+ Contract.Assert(upper >= 0);
+ Contract.Assert(lower >= 0);
+ }
+
+ bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower)
+ {
+ Contract.Assert(newUpper >= newLower);
+ Contract.Assert(newUpper <= nodes.Length);
+ Contract.Assert(newLower <= nodes.Length);
+ Contract.Assert(newUpper >= 0);
+ Contract.Assert(newLower >= 0);
+ Contract.Assert(newUpper >= prevUpper);
+ Contract.Assert(newLower >= prevLower);
+ Contract.Assert(newUpper == prevUpper ^ newLower == prevLower);
+
+ int oldIndexes = (prevUpper << 16) | (prevLower & SixteenBits);
+ int newIndexes = (newUpper << 16) | (newLower & SixteenBits);
+ int prevIndexes = Interlocked.CompareExchange(ref indexes, newIndexes, oldIndexes);
+ prevUpper = (prevIndexes >> 16) & SixteenBits;
+ prevLower = prevIndexes & SixteenBits;
+ return prevIndexes == oldIndexes;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public QueueSegment()
+ {
+ Contract.Assert(QueueSegmentLength <= SixteenBits);
+ nodes = new IThreadPoolWorkItem[QueueSegmentLength];
+ }
+
+
+ public bool IsUsedUp()
+ {
+ int upper, lower;
+ GetIndexes(out upper, out lower);
+ return (upper == nodes.Length) &&
+ (lower == nodes.Length);
+ }
+
+ public bool TryEnqueue(IThreadPoolWorkItem node)
+ {
+ //
+ // If there's room in this segment, atomically increment the upper count (to reserve
+ // space for this node), then store the node.
+ // Note that this leaves a window where it will look like there is data in that
+ // array slot, but it hasn't been written yet. This is taken care of in TryDequeue
+ // with a busy-wait loop, waiting for the element to become non-null. This implies
+ // that we can never store null nodes in this data structure.
+ //
+ Contract.Assert(null != node);
+
+ int upper, lower;
+ GetIndexes(out upper, out lower);
+
+ while (true)
+ {
+ if (upper == nodes.Length)
+ return false;
+
+ if (CompareExchangeIndexes(ref upper, upper + 1, ref lower, lower))
+ {
+ Contract.Assert(Volatile.Read(ref nodes[upper]) == null);
+ Volatile.Write(ref nodes[upper], node);
+ return true;
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public bool TryDequeue(out IThreadPoolWorkItem node)
+ {
+ //
+ // If there are nodes in this segment, increment the lower count, then take the
+ // element we find there.
+ //
+ int upper, lower;
+ GetIndexes(out upper, out lower);
+
+ while(true)
+ {
+ if (lower == upper)
+ {
+ node = null;
+ return false;
+ }
+
+ if (CompareExchangeIndexes(ref upper, upper, ref lower, lower + 1))
+ {
+ // It's possible that a concurrent call to Enqueue hasn't yet
+ // written the node reference to the array. We need to spin until
+ // it shows up.
+ SpinWait spinner = new SpinWait();
+ while ((node = Volatile.Read(ref nodes[lower])) == null)
+ spinner.SpinOnce();
+
+ // Null-out the reference so the object can be GC'd earlier.
+ nodes[lower] = null;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ // The head and tail of the queue. We enqueue to the head, and dequeue from the tail.
+ internal volatile QueueSegment queueHead;
+ internal volatile QueueSegment queueTail;
+ internal bool loggingEnabled;
+
+ internal static SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16);
+
+ private volatile int numOutstandingThreadRequests = 0;
+
+ public ThreadPoolWorkQueue()
+ {
+ queueTail = queueHead = new QueueSegment();
+ loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
+ }
+
+ [SecurityCritical]
+ public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue()
+ {
+ if (null == ThreadPoolWorkQueueThreadLocals.threadLocals)
+ ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
+ return ThreadPoolWorkQueueThreadLocals.threadLocals;
+ }
+
+ [SecurityCritical]
+ internal void EnsureThreadRequested()
+ {
+ //
+ // If we have not yet requested #procs threads from the VM, then request a new thread.
+ // Note that there is a separate count in the VM which will also be incremented in this case,
+ // which is handled by RequestWorkerThread.
+ //
+ int count = numOutstandingThreadRequests;
+ while (count < ThreadPoolGlobals.processorCount)
+ {
+ int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count);
+ if (prev == count)
+ {
+ ThreadPool.RequestWorkerThread();
+ break;
+ }
+ count = prev;
+ }
+ }
+
+ [SecurityCritical]
+ internal void MarkThreadRequestSatisfied()
+ {
+ //
+ // The VM has called us, so one of our outstanding thread requests has been satisfied.
+ // Decrement the count so that future calls to EnsureThreadRequested will succeed.
+ // Note that there is a separate count in the VM which has already been decremented by the VM
+ // by the time we reach this point.
+ //
+ int count = numOutstandingThreadRequests;
+ while (count > 0)
+ {
+ int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
+ if (prev == count)
+ {
+ break;
+ }
+ count = prev;
+ }
+ }
+
+ [SecurityCritical]
+ public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
+ {
+ ThreadPoolWorkQueueThreadLocals tl = null;
+ if (!forceGlobal)
+ tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
+
+ if (loggingEnabled)
+ System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
+
+ if (null != tl)
+ {
+ tl.workStealingQueue.LocalPush(callback);
+ }
+ else
+ {
+ QueueSegment head = queueHead;
+
+ while (!head.TryEnqueue(callback))
+ {
+ Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null);
+
+ while (head.Next != null)
+ {
+ Interlocked.CompareExchange(ref queueHead, head.Next, head);
+ head = queueHead;
+ }
+ }
+ }
+
+ EnsureThreadRequested();
+ }
+
+ [SecurityCritical]
+ internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
+ {
+ ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
+ if (null == tl)
+ return false;
+
+ return tl.workStealingQueue.LocalFindAndPop(callback);
+ }
+
+ [SecurityCritical]
+ public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal)
+ {
+ callback = null;
+ missedSteal = false;
+ WorkStealingQueue wsq = tl.workStealingQueue;
+
+ if (wsq.LocalPop(out callback))
+ Contract.Assert(null != callback);
+
+ if (null == callback)
+ {
+ QueueSegment tail = queueTail;
+ while (true)
+ {
+ if (tail.TryDequeue(out callback))
+ {
+ Contract.Assert(null != callback);
+ break;
+ }
+
+ if (null == tail.Next || !tail.IsUsedUp())
+ {
+ break;
+ }
+ else
+ {
+ Interlocked.CompareExchange(ref queueTail, tail.Next, tail);
+ tail = queueTail;
+ }
+ }
+ }
+
+ if (null == callback)
+ {
+ WorkStealingQueue[] otherQueues = allThreadQueues.Current;
+ int c = otherQueues.Length;
+ int maxIndex = c - 1;
+ int i = tl.random.Next(c);
+ while (c > 0)
+ {
+ i = (i < maxIndex) ? i + 1 : 0;
+ WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i]);
+ if (otherQueue != null &&
+ otherQueue != wsq &&
+ otherQueue.TrySteal(out callback, ref missedSteal))
+ {
+ Contract.Assert(null != callback);
+ break;
+ }
+ c--;
+ }
+ }
+ }
+
+ [SecurityCritical]
+ static internal bool Dispatch()
+ {
+ var workQueue = ThreadPoolGlobals.workQueue;
+ //
+ // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
+ // we need to return to the VM.
+ //
+ int quantumStartTime = Environment.TickCount;
+
+ //
+ // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
+ // From this point on, we are responsible for requesting another thread if we stop working for any
+ // reason, and we believe there might still be work in the queue.
+ //
+ // Note that if this thread is aborted before we get a chance to request another one, the VM will
+ // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
+ //
+ workQueue.MarkThreadRequestSatisfied();
+
+ // Has the desire for logging changed since the last time we entered?
+ workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
+
+ //
+ // Assume that we're going to need another thread if this one returns to the VM. We'll set this to
+ // false later, but only if we're absolutely certain that the queue is empty.
+ //
+ bool needAnotherThread = true;
+ IThreadPoolWorkItem workItem = null;
+ try
+ {
+ //
+ // Set up our thread-local data
+ //
+ ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
+
+ //
+ // Loop until our quantum expires.
+ //
+ while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.tpQuantum)
+ {
+ //
+ // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.
+ // These are fast, so this will not delay aborts/AD-unloads for very long.
+ //
+ try { }
+ finally
+ {
+ bool missedSteal = false;
+ workQueue.Dequeue(tl, out workItem, out missedSteal);
+
+ if (workItem == null)
+ {
+ //
+ // No work. We're going to return to the VM once we leave this protected region.
+ // If we missed a steal, though, there may be more work in the queue.
+ // Instead of looping around and trying again, we'll just request another thread. This way
+ // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
+ // that owns the contended work-stealing queue will pick up its own workitems in the meantime,
+ // which will be more efficient than this thread doing it anyway.
+ //
+ needAnotherThread = missedSteal;
+ }
+ else
+ {
+ //
+ // If we found work, there may be more work. Ask for another thread so that the other work can be processed
+ // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
+ //
+ workQueue.EnsureThreadRequested();
+ }
+ }
+
+ if (workItem == null)
+ {
+ // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
+ return true;
+ }
+ else
+ {
+ if (workQueue.loggingEnabled)
+ System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
+
+ //
+ // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
+ //
+ if (ThreadPoolGlobals.enableWorkerTracking)
+ {
+ bool reportedStatus = false;
+ try
+ {
+ try { }
+ finally
+ {
+ ThreadPool.ReportThreadStatus(true);
+ reportedStatus = true;
+ }
+ workItem.ExecuteWorkItem();
+ workItem = null;
+ }
+ finally
+ {
+ if (reportedStatus)
+ ThreadPool.ReportThreadStatus(false);
+ }
+ }
+ else
+ {
+ workItem.ExecuteWorkItem();
+ workItem = null;
+ }
+
+ //
+ // Notify the VM that we executed this workitem. This is also our opportunity to ask whether Hill Climbing wants
+ // us to return the thread to the pool or not.
+ //
+ if (!ThreadPool.NotifyWorkItemComplete())
+ return false;
+ }
+ }
+ // If we get here, it's because our quantum expired. Tell the VM we're returning normally.
+ return true;
+ }
+ catch (ThreadAbortException tae)
+ {
+ //
+ // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
+ // loop, and the time we execute the work item. QueueUserWorkItemCallback uses this to update its accounting of whether
+ // it was executed or not (in debug builds only). Task uses this to communicate the ThreadAbortException to anyone
+ // who waits for the task to complete.
+ //
+ if (workItem != null)
+ workItem.MarkAborted(tae);
+
+ //
+ // In this case, the VM is going to request another thread on our behalf. No need to do it twice.
+ //
+ needAnotherThread = false;
+ // throw; //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
+ }
+ finally
+ {
+ //
+ // If we are exiting for any reason other than that the queue is definitely empty, ask for another
+ // thread to pick up where we left off.
+ //
+ if (needAnotherThread)
+ workQueue.EnsureThreadRequested();
+ }
+
+ // we can never reach this point, but the C# compiler doesn't know that, because it doesn't know the ThreadAbortException will be reraised above.
+ Contract.Assert(false);
+ return true;
+ }
+ }
+
+ // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
+ internal sealed class ThreadPoolWorkQueueThreadLocals
+ {
+ [ThreadStatic]
+ [SecurityCritical]
+ public static ThreadPoolWorkQueueThreadLocals threadLocals;
+
+ public readonly ThreadPoolWorkQueue workQueue;
+ public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
+ public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId);
+
+ public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
+ {
+ workQueue = tpq;
+ workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
+ ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue);
+ }
+
+ [SecurityCritical]
+ private void CleanUp()
+ {
+ if (null != workStealingQueue)
+ {
+ if (null != workQueue)
+ {
+ bool done = false;
+ while (!done)
+ {
+ // Ensure that we won't be aborted between LocalPop and Enqueue.
+ try { }
+ finally
+ {
+ IThreadPoolWorkItem cb = null;
+ if (workStealingQueue.LocalPop(out cb))
+ {
+ Contract.Assert(null != cb);
+ workQueue.Enqueue(cb, true);
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ }
+ }
+
+ ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue);
+ }
+ }
+
+ [SecuritySafeCritical]
+ ~ThreadPoolWorkQueueThreadLocals()
+ {
+ // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
+ // queue so that they will be executed by another thread, there's no point in doing this cleanup
+ // if we're in the process of shutting down or unloading the AD. In those cases, the work won't
+ // execute anyway. And there are subtle race conditions involved there that would lead us to do the wrong
+ // thing anyway. So we'll only clean up if this is a "normal" finalization.
+ if (!(Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()))
+ CleanUp();
+ }
+ }
+
+ internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
+ {
+ private static IntPtr InvalidHandle
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get
+ {
+ return Win32Native.INVALID_HANDLE_VALUE;
+ }
+ }
+ private IntPtr registeredWaitHandle;
+ private WaitHandle m_internalWaitObject;
+ private bool bReleaseNeeded = false;
+ private volatile int m_lock = 0;
+
+ #if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+ #endif
+ internal RegisteredWaitHandleSafe()
+ {
+ registeredWaitHandle = InvalidHandle;
+ }
+
+ internal IntPtr GetHandle()
+ {
+ return registeredWaitHandle;
+ }
+
+ internal void SetHandle(IntPtr handle)
+ {
+ registeredWaitHandle = handle;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal void SetWaitObject(WaitHandle waitObject)
+ {
+ // needed for DangerousAddRef
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ }
+ finally
+ {
+ m_internalWaitObject = waitObject;
+ if (waitObject != null)
+ {
+ m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
+ }
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal bool Unregister(
+ WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
+ )
+ {
+ bool result = false;
+ // needed for DangerousRelease
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ }
+ finally
+ {
+ // lock(this) cannot be used reliably in Cer since thin lock could be
+ // promoted to syncblock and that is not a guaranteed operation
+ bool bLockTaken = false;
+ do
+ {
+ if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
+ {
+ bLockTaken = true;
+ try
+ {
+ if (ValidHandle())
+ {
+ result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
+ if (result == true)
+ {
+ if (bReleaseNeeded)
+ {
+ m_internalWaitObject.SafeWaitHandle.DangerousRelease();
+ bReleaseNeeded = false;
+ }
+ // if result not true don't release/suppress here so finalizer can make another attempt
+ SetHandle(InvalidHandle);
+ m_internalWaitObject = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+ finally
+ {
+ m_lock = 0;
+ }
+ }
+ Thread.SpinWait(1); // yield to processor
+ }
+ while (!bLockTaken);
+ }
+ return result;
+ }
+
+ private bool ValidHandle()
+ {
+ return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ ~RegisteredWaitHandleSafe()
+ {
+ // if the app has already unregistered the wait, there is nothing to cleanup
+ // we can detect this by checking the handle. Normally, there is no race condition here
+ // so no need to protect reading of handle. However, if this object gets
+ // resurrected and then someone does an unregister, it would introduce a race condition
+ //
+ // PrepareConstrainedRegions call not needed since finalizer already in Cer
+ //
+ // lock(this) cannot be used reliably even in Cer since thin lock could be
+ // promoted to syncblock and that is not a guaranteed operation
+ //
+ // Note that we will not "spin" to get this lock. We make only a single attempt;
+ // if we can't get the lock, it means some other thread is in the middle of a call
+ // to Unregister, which will do the work of the finalizer anyway.
+ //
+ // Further, it's actually critical that we *not* wait for the lock here, because
+ // the other thread that's in the middle of Unregister may be suspended for shutdown.
+ // Then, during the live-object finalization phase of shutdown, this thread would
+ // end up spinning forever, as the other thread would never release the lock.
+ // This will result in a "leak" of sorts (since the handle will not be cleaned up)
+ // but the process is exiting anyway.
+ //
+ // During AD-unload, we don’t finalize live objects until all threads have been
+ // aborted out of the AD. Since these locked regions are CERs, we won’t abort them
+ // while the lock is held. So there should be no leak on AD-unload.
+ //
+ if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
+ {
+ try
+ {
+ if (ValidHandle())
+ {
+ WaitHandleCleanupNative(registeredWaitHandle);
+ if (bReleaseNeeded)
+ {
+ m_internalWaitObject.SafeWaitHandle.DangerousRelease();
+ bReleaseNeeded = false;
+ }
+ SetHandle(InvalidHandle);
+ m_internalWaitObject = null;
+ }
+ }
+ finally
+ {
+ m_lock = 0;
+ }
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void WaitHandleCleanupNative(IntPtr handle);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
+ }
+
+[System.Runtime.InteropServices.ComVisible(true)]
+#if FEATURE_REMOTING
+ public sealed class RegisteredWaitHandle : MarshalByRefObject {
+#else // FEATURE_REMOTING
+ public sealed class RegisteredWaitHandle {
+#endif // FEATURE_REMOTING
+ private RegisteredWaitHandleSafe internalRegisteredWait;
+
+ internal RegisteredWaitHandle()
+ {
+ internalRegisteredWait = new RegisteredWaitHandleSafe();
+ }
+
+ internal void SetHandle(IntPtr handle)
+ {
+ internalRegisteredWait.SetHandle(handle);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal void SetWaitObject(WaitHandle waitObject)
+ {
+ internalRegisteredWait.SetWaitObject(waitObject);
+ }
+
+
+[System.Security.SecuritySafeCritical] // auto-generated
+[System.Runtime.InteropServices.ComVisible(true)]
+ // This is the only public method on this class
+ public bool Unregister(
+ WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
+ )
+ {
+ return internalRegisteredWait.Unregister(waitObject);
+ }
+ }
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public delegate void WaitCallback(Object state);
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public delegate void WaitOrTimerCallback(Object state, bool timedOut); // signalled or timed out
+
+ //
+ // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
+ // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but
+ // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
+ // removed.
+ //
+ internal static class _ThreadPoolWaitCallback
+ {
+ [System.Security.SecurityCritical]
+ static internal bool PerformWaitCallback()
+ {
+ return ThreadPoolWorkQueue.Dispatch();
+ }
+ }
+
+ //
+ // Interface to something that can be queued to the TP. This is implemented by
+ // QueueUserWorkItemCallback, Task, and potentially other internal types.
+ // For example, SemaphoreSlim represents callbacks using its own type that
+ // implements IThreadPoolWorkItem.
+ //
+ // If we decide to expose some of the workstealing
+ // stuff, this is NOT the thing we want to expose to the public.
+ //
+ internal interface IThreadPoolWorkItem
+ {
+ [SecurityCritical]
+ void ExecuteWorkItem();
+ [SecurityCritical]
+ void MarkAborted(ThreadAbortException tae);
+ }
+
+ internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
+ {
+ [System.Security.SecuritySafeCritical]
+ static QueueUserWorkItemCallback() {}
+
+ private WaitCallback callback;
+ private ExecutionContext context;
+ private Object state;
+
+#if DEBUG
+ volatile int executed;
+
+ ~QueueUserWorkItemCallback()
+ {
+ Contract.Assert(
+ executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
+ "A QueueUserWorkItemCallback was never called!");
+ }
+
+ void MarkExecuted(bool aborted)
+ {
+ GC.SuppressFinalize(this);
+ Contract.Assert(
+ 0 == Interlocked.Exchange(ref executed, 1) || aborted,
+ "A QueueUserWorkItemCallback was called twice!");
+ }
+#endif
+
+ [SecurityCritical]
+ internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
+ {
+ callback = waitCallback;
+ state = stateObj;
+ context = ec;
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+#if DEBUG
+ MarkExecuted(false);
+#endif
+ // call directly if it is an unsafe call OR EC flow is suppressed
+ if (context == null)
+ {
+ WaitCallback cb = callback;
+ callback = null;
+ cb(state);
+ }
+ else
+ {
+ ExecutionContext.Run(context, ccb, this, true);
+ }
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
+ {
+#if DEBUG
+ // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
+ // This counts as being executed for our purposes.
+ MarkExecuted(true);
+#endif
+ }
+
+ [System.Security.SecurityCritical]
+ static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
+
+ [System.Security.SecurityCritical]
+ static private void WaitCallback_Context(Object state)
+ {
+ QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
+ WaitCallback wc = obj.callback as WaitCallback;
+ Contract.Assert(null != wc);
+ wc(obj.state);
+ }
+ }
+
+ internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem
+ {
+ [System.Security.SecuritySafeCritical]
+ static QueueUserWorkItemCallbackDefaultContext() { }
+
+ private WaitCallback callback;
+ private Object state;
+
+#if DEBUG
+ private volatile int executed;
+
+ ~QueueUserWorkItemCallbackDefaultContext()
+ {
+ Contract.Assert(
+ executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
+ "A QueueUserWorkItemCallbackDefaultContext was never called!");
+ }
+
+ void MarkExecuted(bool aborted)
+ {
+ GC.SuppressFinalize(this);
+ Contract.Assert(
+ 0 == Interlocked.Exchange(ref executed, 1) || aborted,
+ "A QueueUserWorkItemCallbackDefaultContext was called twice!");
+ }
+#endif
+
+ [SecurityCritical]
+ internal QueueUserWorkItemCallbackDefaultContext(WaitCallback waitCallback, Object stateObj)
+ {
+ callback = waitCallback;
+ state = stateObj;
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.ExecuteWorkItem()
+ {
+#if DEBUG
+ MarkExecuted(false);
+#endif
+ ExecutionContext.Run(ExecutionContext.PreAllocatedDefault, ccb, this, true);
+ }
+
+ [SecurityCritical]
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
+ {
+#if DEBUG
+ // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
+ // This counts as being executed for our purposes.
+ MarkExecuted(true);
+#endif
+ }
+
+ [System.Security.SecurityCritical]
+ static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
+
+ [System.Security.SecurityCritical]
+ static private void WaitCallback_Context(Object state)
+ {
+ QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state;
+ WaitCallback wc = obj.callback as WaitCallback;
+ Contract.Assert(null != wc);
+ obj.callback = null;
+ wc(obj.state);
+ }
+ }
+
+ internal class _ThreadPoolWaitOrTimerCallback
+ {
+ [System.Security.SecuritySafeCritical]
+ static _ThreadPoolWaitOrTimerCallback() {}
+
+ WaitOrTimerCallback _waitOrTimerCallback;
+ ExecutionContext _executionContext;
+ Object _state;
+ [System.Security.SecurityCritical]
+ static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
+ [System.Security.SecurityCritical]
+ static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark)
+ {
+ _waitOrTimerCallback = waitOrTimerCallback;
+ _state = state;
+
+ if (compressStack && !ExecutionContext.IsFlowSuppressed())
+ {
+ // capture the exection context
+ _executionContext = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
+ }
+ }
+
+ [System.Security.SecurityCritical]
+ static private void WaitOrTimerCallback_Context_t(Object state)
+ {
+ WaitOrTimerCallback_Context(state, true);
+ }
+
+ [System.Security.SecurityCritical]
+ static private void WaitOrTimerCallback_Context_f(Object state)
+ {
+ WaitOrTimerCallback_Context(state, false);
+ }
+
+ static private void WaitOrTimerCallback_Context(Object state, bool timedOut)
+ {
+ _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
+ helper._waitOrTimerCallback(helper._state, timedOut);
+ }
+
+ // call back helper
+ [System.Security.SecurityCritical] // auto-generated
+ static internal void PerformWaitOrTimerCallback(Object state, bool timedOut)
+ {
+ _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
+ Contract.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
+ // call directly if it is an unsafe call OR EC flow is suppressed
+ if (helper._executionContext == null)
+ {
+ WaitOrTimerCallback callback = helper._waitOrTimerCallback;
+ callback(helper._state, timedOut);
+ }
+ else
+ {
+ using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
+ {
+ if (timedOut)
+ ExecutionContext.Run(executionContext, _ccbt, helper, true);
+ else
+ ExecutionContext.Run(executionContext, _ccbf, helper, true);
+ }
+ }
+ }
+
+ }
+
+ [System.Security.SecurityCritical]
+ [CLSCompliant(false)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ unsafe public delegate void IOCompletionCallback(uint errorCode, // Error code
+ uint numBytes, // No. of bytes transferred
+ NativeOverlapped* pOVERLAP // ptr to OVERLAP structure
+ );
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ public static class ThreadPool
+ {
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+#pragma warning disable 618
+ [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
+#pragma warning restore 618
+ public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
+ {
+ return SetMaxThreadsNative(workerThreads, completionPortThreads);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
+ {
+ GetMaxThreadsNative(out workerThreads, out completionPortThreads);
+ }
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+#pragma warning disable 618
+ [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
+#pragma warning restore 618
+ public static bool SetMinThreads(int workerThreads, int completionPortThreads)
+ {
+ return SetMinThreadsNative(workerThreads, completionPortThreads);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
+ {
+ GetMinThreadsNative(out workerThreads, out completionPortThreads);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
+ {
+ GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ uint millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [CLSCompliant(false)]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ uint millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
+ }
+
+
+ [System.Security.SecurityCritical] // auto-generated
+ private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ uint millisecondsTimeOutInterval,
+ bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ ref StackCrawlMark stackMark,
+ bool compressStack
+ )
+ {
+#if FEATURE_REMOTING
+ if (RemotingServices.IsTransparentProxy(waitObject))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
+ Contract.EndContractBlock();
+#endif
+
+ RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
+
+ if (callBack != null)
+ {
+ _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark);
+ state = (Object)callBackHelper;
+ // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
+ // this could occur if callback were to fire before SetWaitObject does its addref
+ registeredWaitHandle.SetWaitObject(waitObject);
+ IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
+ state,
+ millisecondsTimeOutInterval,
+ executeOnlyOnce,
+ registeredWaitHandle,
+ ref stackMark,
+ compressStack);
+ registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
+ }
+ else
+ {
+ throw new ArgumentNullException("WaitOrTimerCallback");
+ }
+ return registeredWaitHandle;
+ }
+
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ int millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ if (millisecondsTimeOutInterval < -1)
+ throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ int millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ if (millisecondsTimeOutInterval < -1)
+ throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ long millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ if (millisecondsTimeOutInterval < -1)
+ throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ long millisecondsTimeOutInterval,
+ bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
+ )
+ {
+ if (millisecondsTimeOutInterval < -1)
+ throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle RegisterWaitForSingleObject(
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ TimeSpan timeout,
+ bool executeOnlyOnce
+ )
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
+ WaitHandle waitObject,
+ WaitOrTimerCallback callBack,
+ Object state,
+ TimeSpan timeout,
+ bool executeOnlyOnce
+ )
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (tm < -1)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (tm > (long) Int32.MaxValue)
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static bool QueueUserWorkItem(
+ WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
+ Object state
+ )
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return QueueUserWorkItemHelper(callBack,state,ref stackMark,true);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static bool QueueUserWorkItem(
+ WaitCallback callBack // NOTE: we do not expose options that allow the callback to be queued as an APC
+ )
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return QueueUserWorkItemHelper(callBack,null,ref stackMark,true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public static bool UnsafeQueueUserWorkItem(
+ WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
+ Object state
+ )
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return QueueUserWorkItemHelper(callBack,state,ref stackMark,false);
+ }
+
+ //ThreadPool has per-appdomain managed queue of work-items. The VM is
+ //responsible for just scheduling threads into appdomains. After that
+ //work-items are dispatched from the managed queue.
+ [System.Security.SecurityCritical] // auto-generated
+ private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack )
+ {
+ bool success = true;
+
+ if (callBack != null)
+ {
+ //The thread pool maintains a per-appdomain managed work queue.
+ //New thread pool entries are added in the managed queue.
+ //The VM is responsible for the actual growing/shrinking of
+ //threads.
+
+ EnsureVMInitialized();
+
+ //
+ // If we are able to create the workitem, we need to get it in the queue without being interrupted
+ // by a ThreadAbortException.
+ //
+ try { }
+ finally
+ {
+ ExecutionContext context = compressStack && !ExecutionContext.IsFlowSuppressed() ?
+ ExecutionContext.Capture(ref stackMark, ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase) :
+ null;
+
+ IThreadPoolWorkItem tpcallBack = context == ExecutionContext.PreAllocatedDefault ?
+ new QueueUserWorkItemCallbackDefaultContext(callBack, state) :
+ (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context);
+
+ ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, true);
+ success = true;
+ }
+ }
+ else
+ {
+ throw new ArgumentNullException("WaitCallback");
+ }
+ return success;
+ }
+
+ [SecurityCritical]
+ internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
+ {
+ Contract.Assert(null != workItem);
+ EnsureVMInitialized();
+
+ //
+ // Enqueue needs to be protected from ThreadAbort
+ //
+ try { }
+ finally
+ {
+ ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
+ }
+ }
+
+ // This method tries to take the target callback out of the current thread's queue.
+ [SecurityCritical]
+ internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
+ {
+ Contract.Assert(null != workItem);
+ if (!ThreadPoolGlobals.vmTpInitialized)
+ return false; //Not initialized, so there's no way this workitem was ever queued.
+ return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
+ }
+
+ // Get all workitems. Called by TaskScheduler in its debugger hooks.
+ [SecurityCritical]
+ internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
+ {
+ return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail);
+ }
+
+ internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail)
+ {
+ if (wsQueues != null)
+ {
+ // First, enumerate all workitems in thread-local queues.
+ foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues)
+ {
+ if (wsq != null && wsq.m_array != null)
+ {
+ IThreadPoolWorkItem[] items = wsq.m_array;
+ for (int i = 0; i < items.Length; i++)
+ {
+ IThreadPoolWorkItem item = items[i];
+ if (item != null)
+ yield return item;
+ }
+ }
+ }
+ }
+
+ if (globalQueueTail != null)
+ {
+ // Now the global queue
+ for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail;
+ segment != null;
+ segment = segment.Next)
+ {
+ IThreadPoolWorkItem[] items = segment.nodes;
+ for (int i = 0; i < items.Length; i++)
+ {
+ IThreadPoolWorkItem item = items[i];
+ if (item != null)
+ yield return item;
+ }
+ }
+ }
+ }
+
+ [SecurityCritical]
+ internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
+ {
+ return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null);
+ }
+
+ [SecurityCritical]
+ internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems()
+ {
+ return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail);
+ }
+
+ private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
+ {
+ int i = 0;
+ foreach (IThreadPoolWorkItem item in workitems)
+ {
+ i++;
+ }
+
+ object[] result = new object[i];
+ i = 0;
+ foreach (IThreadPoolWorkItem item in workitems)
+ {
+ if (i < result.Length) //just in case someone calls us while the queues are in motion
+ result[i] = item;
+ i++;
+ }
+
+ return result;
+ }
+
+ // This is the method the debugger will actually call, if it ends up calling
+ // into ThreadPool directly. Tests can use this to simulate a debugger, as well.
+ [SecurityCritical]
+ internal static object[] GetQueuedWorkItemsForDebugger()
+ {
+ return ToObjectArray(GetQueuedWorkItems());
+ }
+
+ [SecurityCritical]
+ internal static object[] GetGloballyQueuedWorkItemsForDebugger()
+ {
+ return ToObjectArray(GetGloballyQueuedWorkItems());
+ }
+
+ [SecurityCritical]
+ internal static object[] GetLocallyQueuedWorkItemsForDebugger()
+ {
+ return ToObjectArray(GetLocallyQueuedWorkItems());
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ internal static extern bool RequestWorkerThread();
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ [CLSCompliant(false)]
+ unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
+ {
+ return PostQueuedCompletionStatus(overlapped);
+ }
+
+ [SecurityCritical]
+ private static void EnsureVMInitialized()
+ {
+ if (!ThreadPoolGlobals.vmTpInitialized)
+ {
+ ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
+ ThreadPoolGlobals.vmTpInitialized = true;
+ }
+ }
+
+ // Native methods:
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern bool NotifyWorkItemComplete();
+
+ [System.Security.SecurityCritical]
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern void ReportThreadStatus(bool isWorking);
+
+ [System.Security.SecuritySafeCritical]
+ internal static void NotifyWorkItemProgress()
+ {
+ if (!ThreadPoolGlobals.vmTpInitialized)
+ ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
+ NotifyWorkItemProgressNative();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern void NotifyWorkItemProgressNative();
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern bool IsThreadPoolHosted();
+
+ [System.Security.SecurityCritical] // auto-generated
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void InitializeVMTp(ref bool enableWorkerTracking);
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern IntPtr RegisterWaitForSingleObjectNative(
+ WaitHandle waitHandle,
+ Object state,
+ uint timeOutInterval,
+ bool executeOnlyOnce,
+ RegisteredWaitHandle registeredWaitHandle,
+ ref StackCrawlMark stackMark,
+ bool compressStack
+ );
+
+#if !FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
+ [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static bool BindHandle(
+ IntPtr osHandle
+ )
+ {
+ return BindIOCompletionCallbackNative(osHandle);
+ }
+#endif
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+#pragma warning disable 618
+ [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+#pragma warning restore 618
+ public static bool BindHandle(SafeHandle osHandle)
+ {
+ if (osHandle == null)
+ throw new ArgumentNullException("osHandle");
+
+ bool ret = false;
+ bool mustReleaseSafeHandle = false;
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try {
+ osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
+ ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
+ }
+ finally {
+ if (mustReleaseSafeHandle)
+ osHandle.DangerousRelease();
+ }
+ return ret;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadPriority.cs b/src/mscorlib/src/System/Threading/ThreadPriority.cs
new file mode 100644
index 0000000000..c56156eb89
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadPriority.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Enums for the priorities of a Thread
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+ using System.Threading;
+
+ [Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public enum ThreadPriority
+ {
+ /*=========================================================================
+ ** Constants for thread priorities.
+ =========================================================================*/
+ Lowest = 0,
+ BelowNormal = 1,
+ Normal = 2,
+ AboveNormal = 3,
+ Highest = 4
+
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadStart.cs b/src/mscorlib/src/System/Threading/ThreadStart.cs
new file mode 100644
index 0000000000..b968117195
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadStart.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: This class is a Delegate which defines the start method
+** for starting a thread. That method must match this delegate.
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+ using System.Security.Permissions;
+ using System.Threading;
+
+ // Define the delegate
+ // NOTE: If you change the signature here, there is code in COMSynchronization
+ // that invokes this delegate in native.
+[System.Runtime.InteropServices.ComVisible(true)]
+ public delegate void ThreadStart();
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadStartException.cs b/src/mscorlib/src/System/Threading/ThreadStartException.cs
new file mode 100644
index 0000000000..33fb460b3d
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadStartException.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Threading
+{
+ using System;
+ using System.Runtime.Serialization;
+ using System.Runtime.InteropServices;
+
+ [Serializable]
+ public sealed class ThreadStartException : SystemException
+ {
+ private ThreadStartException()
+ : base(Environment.GetResourceString("Arg_ThreadStartException"))
+ {
+ SetErrorCode(__HResults.COR_E_THREADSTART);
+ }
+
+ private ThreadStartException(Exception reason)
+ : base(Environment.GetResourceString("Arg_ThreadStartException"), reason)
+ {
+ SetErrorCode(__HResults.COR_E_THREADSTART);
+ }
+
+ //required for serialization
+ internal ThreadStartException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ }
+}
+
+
diff --git a/src/mscorlib/src/System/Threading/ThreadState.cs b/src/mscorlib/src/System/Threading/ThreadState.cs
new file mode 100644
index 0000000000..007e1bf6e9
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadState.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Enum to represent the different thread states
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+
+[Serializable]
+[Flags]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public enum ThreadState
+ {
+ /*=========================================================================
+ ** Constants for thread states.
+ =========================================================================*/
+ Running = 0,
+ StopRequested = 1,
+ SuspendRequested = 2,
+ Background = 4,
+ Unstarted = 8,
+ Stopped = 16,
+ WaitSleepJoin = 32,
+ Suspended = 64,
+ AbortRequested = 128,
+ Aborted = 256
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/ThreadStateException.cs b/src/mscorlib/src/System/Threading/ThreadStateException.cs
new file mode 100644
index 0000000000..535dffcdbf
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadStateException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: An exception class to indicate that the Thread class is in an
+** invalid state for the method.
+**
+**
+=============================================================================*/
+
+namespace System.Threading {
+ using System;
+ using System.Runtime.Serialization;
+ [System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public class ThreadStateException : SystemException {
+ public ThreadStateException()
+ : base(Environment.GetResourceString("Arg_ThreadStateException")) {
+ SetErrorCode(__HResults.COR_E_THREADSTATE);
+ }
+
+ public ThreadStateException(String message)
+ : base(message) {
+ SetErrorCode(__HResults.COR_E_THREADSTATE);
+ }
+
+ public ThreadStateException(String message, Exception innerException)
+ : base(message, innerException) {
+ SetErrorCode(__HResults.COR_E_THREADSTATE);
+ }
+
+ protected ThreadStateException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Timeout.cs b/src/mscorlib/src/System/Threading/Timeout.cs
new file mode 100644
index 0000000000..99e24159b2
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Timeout.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading {
+ using System.Threading;
+ using System;
+ // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep
+ // etc) to indicate that no timeout should occur.
+ //
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public static class Timeout
+ {
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Timeout.Infinite);
+
+ public const int Infinite = -1;
+ internal const uint UnsignedInfinite = unchecked((uint)-1);
+ }
+
+}
diff --git a/src/mscorlib/src/System/Threading/Timer.cs b/src/mscorlib/src/System/Threading/Timer.cs
new file mode 100644
index 0000000000..cb08c6e033
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Timer.cs
@@ -0,0 +1,954 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Threading
+{
+ using System;
+ using System.Security;
+ using System.Security.Permissions;
+ using Microsoft.Win32;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.Tracing;
+ using Microsoft.Win32.SafeHandles;
+
+
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public delegate void TimerCallback(Object state);
+
+ //
+ // TimerQueue maintains a list of active timers in this AppDomain. We use a single native timer, supplied by the VM,
+ // to schedule all managed timers in the AppDomain.
+ //
+ // Perf assumptions: We assume that timers are created and destroyed frequently, but rarely actually fire.
+ // There are roughly two types of timer:
+ //
+ // - timeouts for operations. These are created and destroyed very frequently, but almost never fire, because
+ // the whole point is that the timer only fires if something has gone wrong.
+ //
+ // - scheduled background tasks. These typically do fire, but they usually have quite long durations.
+ // So the impact of spending a few extra cycles to fire these is negligible.
+ //
+ // Because of this, we want to choose a data structure with very fast insert and delete times, but we can live
+ // with linear traversal times when firing timers.
+ //
+ // The data structure we've chosen is an unordered doubly-linked list of active timers. This gives O(1) insertion
+ // and removal, and O(N) traversal when finding expired timers.
+ //
+ // Note that all instance methods of this class require that the caller hold a lock on TimerQueue.Instance.
+ //
+ class TimerQueue
+ {
+ #region singleton pattern implementation
+
+ // The one-and-only TimerQueue for the AppDomain.
+ static TimerQueue s_queue = new TimerQueue();
+
+ public static TimerQueue Instance
+ {
+ get { return s_queue; }
+ }
+
+ private TimerQueue()
+ {
+ // empty private constructor to ensure we remain a singleton.
+ }
+
+ #endregion
+
+ #region interface to native per-AppDomain timer
+
+ //
+ // We need to keep our notion of time synchronized with the calls to SleepEx that drive
+ // the underlying native timer. In Win8, SleepEx does not count the time the machine spends
+ // sleeping/hibernating. Environment.TickCount (GetTickCount) *does* count that time,
+ // so we will get out of sync with SleepEx if we use that method.
+ //
+ // So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
+ // in sleep/hibernate mode.
+ //
+ private static int TickCount
+ {
+ [SecuritySafeCritical]
+ get
+ {
+#if !FEATURE_PAL
+ if (Environment.IsWindows8OrAbove)
+ {
+ ulong time100ns;
+
+ bool result = Win32Native.QueryUnbiasedInterruptTime(out time100ns);
+ if (!result)
+ throw Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
+
+ // convert to 100ns to milliseconds, and truncate to 32 bits.
+ return (int)(uint)(time100ns / 10000);
+ }
+ else
+#endif
+ {
+ return Environment.TickCount;
+ }
+ }
+ }
+
+ //
+ // We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded.
+ //
+ [SecurityCritical]
+ class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ public AppDomainTimerSafeHandle()
+ : base(true)
+ {
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ protected override bool ReleaseHandle()
+ {
+ return DeleteAppDomainTimer(handle);
+ }
+ }
+
+ [SecurityCritical]
+ AppDomainTimerSafeHandle m_appDomainTimer;
+
+ bool m_isAppDomainTimerScheduled;
+ int m_currentAppDomainTimerStartTicks;
+ uint m_currentAppDomainTimerDuration;
+
+ [SecuritySafeCritical]
+ private bool EnsureAppDomainTimerFiresBy(uint requestedDuration)
+ {
+ //
+ // The VM's timer implementation does not work well for very long-duration timers.
+ // See kb 950807.
+ // So we'll limit our native timer duration to a "small" value.
+ // This may cause us to attempt to fire timers early, but that's ok -
+ // we'll just see that none of our timers has actually reached its due time,
+ // and schedule the native timer again.
+ //
+ const uint maxPossibleDuration = 0x0fffffff;
+ uint actualDuration = Math.Min(requestedDuration, maxPossibleDuration);
+
+ if (m_isAppDomainTimerScheduled)
+ {
+ uint elapsed = (uint)(TickCount - m_currentAppDomainTimerStartTicks);
+ if (elapsed >= m_currentAppDomainTimerDuration)
+ return true; //the timer's about to fire
+
+ uint remainingDuration = m_currentAppDomainTimerDuration - elapsed;
+ if (actualDuration >= remainingDuration)
+ return true; //the timer will fire earlier than this request
+ }
+
+ // If Pause is underway then do not schedule the timers
+ // A later update during resume will re-schedule
+ if(m_pauseTicks != 0)
+ {
+ Contract.Assert(!m_isAppDomainTimerScheduled);
+ Contract.Assert(m_appDomainTimer == null);
+ return true;
+ }
+
+ if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid)
+ {
+ Contract.Assert(!m_isAppDomainTimerScheduled);
+
+ m_appDomainTimer = CreateAppDomainTimer(actualDuration);
+ if (!m_appDomainTimer.IsInvalid)
+ {
+ m_isAppDomainTimerScheduled = true;
+ m_currentAppDomainTimerStartTicks = TickCount;
+ m_currentAppDomainTimerDuration = actualDuration;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (ChangeAppDomainTimer(m_appDomainTimer, actualDuration))
+ {
+ m_isAppDomainTimerScheduled = true;
+ m_currentAppDomainTimerStartTicks = TickCount;
+ m_currentAppDomainTimerDuration = actualDuration;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ //
+ // The VM calls this when the native timer fires.
+ //
+ [SecuritySafeCritical]
+ internal static void AppDomainTimerCallback()
+ {
+ Instance.FireNextTimers();
+ }
+
+ [System.Security.SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime);
+
+ [System.Security.SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime);
+
+ [System.Security.SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ static extern bool DeleteAppDomainTimer(IntPtr handle);
+
+ #endregion
+
+ #region Firing timers
+
+ //
+ // The list of timers
+ //
+ TimerQueueTimer m_timers;
+
+
+ volatile int m_pauseTicks = 0; // Time when Pause was called
+
+ [SecurityCritical]
+ internal void Pause()
+ {
+ lock(this)
+ {
+ // Delete the native timer so that no timers are fired in the Pause zone
+ if(m_appDomainTimer != null && !m_appDomainTimer.IsInvalid)
+ {
+ m_appDomainTimer.Dispose();
+ m_appDomainTimer = null;
+ m_isAppDomainTimerScheduled = false;
+ m_pauseTicks = TickCount;
+ }
+ }
+ }
+
+ [SecurityCritical]
+ internal void Resume()
+ {
+ //
+ // Update timers to adjust their due-time to accomodate Pause/Resume
+ //
+ lock (this)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ int pauseTicks = m_pauseTicks;
+ m_pauseTicks = 0; // Set this to 0 so that now timers can be scheduled
+
+ int resumedTicks = TickCount;
+ int pauseDuration = resumedTicks - pauseTicks;
+
+ bool haveTimerToSchedule = false;
+ uint nextAppDomainTimerDuration = uint.MaxValue;
+
+ TimerQueueTimer timer = m_timers;
+ while (timer != null)
+ {
+ Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
+ Contract.Assert(resumedTicks >= timer.m_startTicks);
+
+ uint elapsed; // How much of the timer dueTime has already elapsed
+
+ // Timers started before the paused event has to be sufficiently delayed to accomodate
+ // for the Pause time. However, timers started after the Paused event shouldnt be adjusted.
+ // E.g. ones created by the app in its Activated event should fire when it was designated.
+ // The Resumed event which is where this routine is executing is after this Activated and hence
+ // shouldn't delay this timer
+
+ if(timer.m_startTicks <= pauseTicks)
+ elapsed = (uint)(pauseTicks - timer.m_startTicks);
+ else
+ elapsed = (uint)(resumedTicks - timer.m_startTicks);
+
+ // Handling the corner cases where a Timer was already due by the time Resume is happening,
+ // We shouldn't delay those timers.
+ // Example is a timer started in App's Activated event with a very small duration
+ timer.m_dueTime = (timer.m_dueTime > elapsed) ? timer.m_dueTime - elapsed : 0;;
+ timer.m_startTicks = resumedTicks; // re-baseline
+
+ if (timer.m_dueTime < nextAppDomainTimerDuration)
+ {
+ haveTimerToSchedule = true;
+ nextAppDomainTimerDuration = timer.m_dueTime;
+ }
+
+ timer = timer.m_next;
+ }
+
+ if (haveTimerToSchedule)
+ {
+ EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
+ }
+ }
+ }
+ }
+
+
+ //
+ // Fire any timers that have expired, and update the native timer to schedule the rest of them.
+ //
+ private void FireNextTimers()
+ {
+ //
+ // we fire the first timer on this thread; any other timers that might have fired are queued
+ // to the ThreadPool.
+ //
+ TimerQueueTimer timerToFireOnThisThread = null;
+
+ lock (this)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ //
+ // since we got here, that means our previous timer has fired.
+ //
+ m_isAppDomainTimerScheduled = false;
+ bool haveTimerToSchedule = false;
+ uint nextAppDomainTimerDuration = uint.MaxValue;
+
+ int nowTicks = TickCount;
+
+ //
+ // Sweep through all timers. The ones that have reached their due time
+ // will fire. We will calculate the next native timer due time from the
+ // other timers.
+ //
+ TimerQueueTimer timer = m_timers;
+ while (timer != null)
+ {
+ Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
+
+ uint elapsed = (uint)(nowTicks - timer.m_startTicks);
+ if (elapsed >= timer.m_dueTime)
+ {
+ //
+ // Remember the next timer in case we delete this one
+ //
+ TimerQueueTimer nextTimer = timer.m_next;
+
+ if (timer.m_period != Timeout.UnsignedInfinite)
+ {
+ timer.m_startTicks = nowTicks;
+ timer.m_dueTime = timer.m_period;
+
+ //
+ // This is a repeating timer; schedule it to run again.
+ //
+ if (timer.m_dueTime < nextAppDomainTimerDuration)
+ {
+ haveTimerToSchedule = true;
+ nextAppDomainTimerDuration = timer.m_dueTime;
+ }
+ }
+ else
+ {
+ //
+ // Not repeating; remove it from the queue
+ //
+ DeleteTimer(timer);
+ }
+
+ //
+ // If this is the first timer, we'll fire it on this thread. Otherwise, queue it
+ // to the ThreadPool.
+ //
+ if (timerToFireOnThisThread == null)
+ timerToFireOnThisThread = timer;
+ else
+ QueueTimerCompletion(timer);
+
+ timer = nextTimer;
+ }
+ else
+ {
+ //
+ // This timer hasn't fired yet. Just update the next time the native timer fires.
+ //
+ uint remaining = timer.m_dueTime - elapsed;
+ if (remaining < nextAppDomainTimerDuration)
+ {
+ haveTimerToSchedule = true;
+ nextAppDomainTimerDuration = remaining;
+ }
+ timer = timer.m_next;
+ }
+ }
+
+ if (haveTimerToSchedule)
+ EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
+ }
+ }
+
+ //
+ // Fire the user timer outside of the lock!
+ //
+ if (timerToFireOnThisThread != null)
+ timerToFireOnThisThread.Fire();
+ }
+
+ [SecuritySafeCritical]
+ private static void QueueTimerCompletion(TimerQueueTimer timer)
+ {
+ WaitCallback callback = s_fireQueuedTimerCompletion;
+ if (callback == null)
+ s_fireQueuedTimerCompletion = callback = new WaitCallback(FireQueuedTimerCompletion);
+
+ // Can use "unsafe" variant because we take care of capturing and restoring
+ // the ExecutionContext.
+ ThreadPool.UnsafeQueueUserWorkItem(callback, timer);
+ }
+
+ private static WaitCallback s_fireQueuedTimerCompletion;
+
+ private static void FireQueuedTimerCompletion(object state)
+ {
+ ((TimerQueueTimer)state).Fire();
+ }
+
+ #endregion
+
+ #region Queue implementation
+
+ public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
+ {
+ if (timer.m_dueTime == Timeout.UnsignedInfinite)
+ {
+ // the timer is not in the list; add it (as the head of the list).
+ timer.m_next = m_timers;
+ timer.m_prev = null;
+ if (timer.m_next != null)
+ timer.m_next.m_prev = timer;
+ m_timers = timer;
+ }
+ timer.m_dueTime = dueTime;
+ timer.m_period = (period == 0) ? Timeout.UnsignedInfinite : period;
+ timer.m_startTicks = TickCount;
+ return EnsureAppDomainTimerFiresBy(dueTime);
+ }
+
+ public void DeleteTimer(TimerQueueTimer timer)
+ {
+ if (timer.m_dueTime != Timeout.UnsignedInfinite)
+ {
+ if (timer.m_next != null)
+ timer.m_next.m_prev = timer.m_prev;
+ if (timer.m_prev != null)
+ timer.m_prev.m_next = timer.m_next;
+ if (m_timers == timer)
+ m_timers = timer.m_next;
+
+ timer.m_dueTime = Timeout.UnsignedInfinite;
+ timer.m_period = Timeout.UnsignedInfinite;
+ timer.m_startTicks = 0;
+ timer.m_prev = null;
+ timer.m_next = null;
+ }
+ }
+
+ #endregion
+ }
+
+ //
+ // A timer in our TimerQueue.
+ //
+ sealed class TimerQueueTimer
+ {
+ //
+ // All fields of this class are protected by a lock on TimerQueue.Instance.
+ //
+ // The first four fields are maintained by TimerQueue itself.
+ //
+ internal TimerQueueTimer m_next;
+ internal TimerQueueTimer m_prev;
+
+ //
+ // The time, according to TimerQueue.TickCount, when this timer's current interval started.
+ //
+ internal int m_startTicks;
+
+ //
+ // Timeout.UnsignedInfinite if we are not going to fire. Otherwise, the offset from m_startTime when we will fire.
+ //
+ internal uint m_dueTime;
+
+ //
+ // Timeout.UnsignedInfinite if we are a single-shot timer. Otherwise, the repeat interval.
+ //
+ internal uint m_period;
+
+ //
+ // Info about the user's callback
+ //
+ readonly TimerCallback m_timerCallback;
+ readonly Object m_state;
+ readonly ExecutionContext m_executionContext;
+
+
+ //
+ // When Timer.Dispose(WaitHandle) is used, we need to signal the wait handle only
+ // after all pending callbacks are complete. We set m_canceled to prevent any callbacks that
+ // are already queued from running. We track the number of callbacks currently executing in
+ // m_callbacksRunning. We set m_notifyWhenNoCallbacksRunning only when m_callbacksRunning
+ // reaches zero.
+ //
+ int m_callbacksRunning;
+ volatile bool m_canceled;
+ volatile WaitHandle m_notifyWhenNoCallbacksRunning;
+
+
+ [SecurityCritical]
+ internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark)
+ {
+ m_timerCallback = timerCallback;
+ m_state = state;
+ m_dueTime = Timeout.UnsignedInfinite;
+ m_period = Timeout.UnsignedInfinite;
+
+ if (!ExecutionContext.IsFlowSuppressed())
+ {
+ m_executionContext = ExecutionContext.Capture(
+ ref stackMark,
+ ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
+ }
+
+ //
+ // After the following statement, the timer may fire. No more manipulation of timer state outside of
+ // the lock is permitted beyond this point!
+ //
+ if (dueTime != Timeout.UnsignedInfinite)
+ Change(dueTime, period);
+ }
+
+
+ internal bool Change(uint dueTime, uint period)
+ {
+ bool success;
+
+ lock (TimerQueue.Instance)
+ {
+ if (m_canceled)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
+
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ m_period = period;
+
+ if (dueTime == Timeout.UnsignedInfinite)
+ {
+ TimerQueue.Instance.DeleteTimer(this);
+ success = true;
+ }
+ else
+ {
+ if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
+ FrameworkEventSource.Log.ThreadTransferSendObj(this, 1, string.Empty, true);
+
+ success = TimerQueue.Instance.UpdateTimer(this, dueTime, period);
+ }
+ }
+ }
+
+ return success;
+ }
+
+
+ public void Close()
+ {
+ lock (TimerQueue.Instance)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ if (!m_canceled)
+ {
+ m_canceled = true;
+ TimerQueue.Instance.DeleteTimer(this);
+ }
+ }
+ }
+ }
+
+
+ public bool Close(WaitHandle toSignal)
+ {
+ bool success;
+ bool shouldSignal = false;
+
+ lock (TimerQueue.Instance)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ if (m_canceled)
+ {
+ success = false;
+ }
+ else
+ {
+ m_canceled = true;
+ m_notifyWhenNoCallbacksRunning = toSignal;
+ TimerQueue.Instance.DeleteTimer(this);
+
+ if (m_callbacksRunning == 0)
+ shouldSignal = true;
+
+ success = true;
+ }
+ }
+ }
+
+ if (shouldSignal)
+ SignalNoCallbacksRunning();
+
+ return success;
+ }
+
+
+ internal void Fire()
+ {
+ bool canceled = false;
+
+ lock (TimerQueue.Instance)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ canceled = m_canceled;
+ if (!canceled)
+ m_callbacksRunning++;
+ }
+ }
+
+ if (canceled)
+ return;
+
+ CallCallback();
+
+ bool shouldSignal = false;
+ lock (TimerQueue.Instance)
+ {
+ // prevent ThreadAbort while updating state
+ try { }
+ finally
+ {
+ m_callbacksRunning--;
+ if (m_canceled && m_callbacksRunning == 0 && m_notifyWhenNoCallbacksRunning != null)
+ shouldSignal = true;
+ }
+ }
+
+ if (shouldSignal)
+ SignalNoCallbacksRunning();
+ }
+
+ [SecuritySafeCritical]
+ internal void SignalNoCallbacksRunning()
+ {
+ Win32Native.SetEvent(m_notifyWhenNoCallbacksRunning.SafeWaitHandle);
+ }
+
+ [SecuritySafeCritical]
+ internal void CallCallback()
+ {
+ if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
+ FrameworkEventSource.Log.ThreadTransferReceiveObj(this, 1, string.Empty);
+
+ // call directly if EC flow is suppressed
+ if (m_executionContext == null)
+ {
+ m_timerCallback(m_state);
+ }
+ else
+ {
+ using (ExecutionContext executionContext =
+ m_executionContext.IsPreAllocatedDefault ? m_executionContext : m_executionContext.CreateCopy())
+ {
+ ContextCallback callback = s_callCallbackInContext;
+ if (callback == null)
+ s_callCallbackInContext = callback = new ContextCallback(CallCallbackInContext);
+
+ ExecutionContext.Run(
+ executionContext,
+ callback,
+ this, // state
+ true); // ignoreSyncCtx
+ }
+ }
+ }
+
+ [SecurityCritical]
+ private static ContextCallback s_callCallbackInContext;
+
+ [SecurityCritical]
+ private static void CallCallbackInContext(object state)
+ {
+ TimerQueueTimer t = (TimerQueueTimer)state;
+ t.m_timerCallback(t.m_state);
+ }
+ }
+
+ //
+ // TimerHolder serves as an intermediary between Timer and TimerQueueTimer, releasing the TimerQueueTimer
+ // if the Timer is collected.
+ // This is necessary because Timer itself cannot use its finalizer for this purpose. If it did,
+ // then users could control timer lifetimes using GC.SuppressFinalize/ReRegisterForFinalize.
+ // You might ask, wouldn't that be a good thing? Maybe (though it would be even better to offer this
+ // via first-class APIs), but Timer has never offered this, and adding it now would be a breaking
+ // change, because any code that happened to be suppressing finalization of Timer objects would now
+ // unwittingly be changing the lifetime of those timers.
+ //
+ sealed class TimerHolder
+ {
+ internal TimerQueueTimer m_timer;
+
+ public TimerHolder(TimerQueueTimer timer)
+ {
+ m_timer = timer;
+ }
+
+ ~TimerHolder()
+ {
+ //
+ // If shutdown has started, another thread may be suspended while holding the timer lock.
+ // So we can't safely close the timer.
+ //
+ // Similarly, we should not close the timer during AD-unload's live-object finalization phase.
+ // A rude abort may have prevented us from releasing the lock.
+ //
+ // Note that in either case, the Timer still won't fire, because ThreadPool threads won't be
+ // allowed to run in this AppDomain.
+ //
+ if (Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload())
+ return;
+
+ m_timer.Close();
+ }
+
+ public void Close()
+ {
+ m_timer.Close();
+ GC.SuppressFinalize(this);
+ }
+
+ public bool Close(WaitHandle notifyObject)
+ {
+ bool result = m_timer.Close(notifyObject);
+ GC.SuppressFinalize(this);
+ return result;
+ }
+
+ }
+
+
+ [HostProtection(Synchronization=true, ExternalThreading=true)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+#if FEATURE_REMOTING
+ public sealed class Timer : MarshalByRefObject, IDisposable
+#else // FEATURE_REMOTING
+ public sealed class Timer : IDisposable
+#endif // FEATURE_REMOTING
+ {
+ private const UInt32 MAX_SUPPORTED_TIMEOUT = (uint)0xfffffffe;
+
+ private TimerHolder m_timer;
+
+ [SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public Timer(TimerCallback callback,
+ Object state,
+ int dueTime,
+ int period)
+ {
+ if (dueTime < -1)
+ throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (period < -1 )
+ throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+
+ TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period,ref stackMark);
+ }
+
+ [SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public Timer(TimerCallback callback,
+ Object state,
+ TimeSpan dueTime,
+ TimeSpan period)
+ {
+ long dueTm = (long)dueTime.TotalMilliseconds;
+ if (dueTm < -1)
+ throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (dueTm > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
+
+ long periodTm = (long)period.TotalMilliseconds;
+ if (periodTm < -1)
+ throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (periodTm > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm,ref stackMark);
+ }
+
+ [CLSCompliant(false)]
+ [SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public Timer(TimerCallback callback,
+ Object state,
+ UInt32 dueTime,
+ UInt32 period)
+ {
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ TimerSetup(callback,state,dueTime,period,ref stackMark);
+ }
+
+ [SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public Timer(TimerCallback callback,
+ Object state,
+ long dueTime,
+ long period)
+ {
+ if (dueTime < -1)
+ throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (period < -1)
+ throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (dueTime > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
+ if (period > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
+ Contract.EndContractBlock();
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ TimerSetup(callback,state,(UInt32) dueTime, (UInt32) period,ref stackMark);
+ }
+
+ [SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public Timer(TimerCallback callback)
+ {
+ int dueTime = -1; // we want timer to be registered, but not activated. Requires caller to call
+ int period = -1; // Change after a timer instance is created. This is to avoid the potential
+ // for a timer to be fired before the returned value is assigned to the variable,
+ // potentially causing the callback to reference a bogus value (if passing the timer to the callback).
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
+ }
+
+ [SecurityCritical]
+ private void TimerSetup(TimerCallback callback,
+ Object state,
+ UInt32 dueTime,
+ UInt32 period,
+ ref StackCrawlMark stackMark)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("TimerCallback");
+ Contract.EndContractBlock();
+
+ m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, ref stackMark));
+ }
+
+ [SecurityCritical]
+ internal static void Pause()
+ {
+ TimerQueue.Instance.Pause();
+ }
+
+ [SecurityCritical]
+ internal static void Resume()
+ {
+ TimerQueue.Instance.Resume();
+ }
+
+ public bool Change(int dueTime, int period)
+ {
+ if (dueTime < -1 )
+ throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (period < -1)
+ throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ Contract.EndContractBlock();
+
+ return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
+ }
+
+ public bool Change(TimeSpan dueTime, TimeSpan period)
+ {
+ return Change((long) dueTime.TotalMilliseconds, (long) period.TotalMilliseconds);
+ }
+
+ [CLSCompliant(false)]
+ public bool Change(UInt32 dueTime, UInt32 period)
+ {
+ return m_timer.m_timer.Change(dueTime, period);
+ }
+
+ public bool Change(long dueTime, long period)
+ {
+ if (dueTime < -1 )
+ throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (period < -1)
+ throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ if (dueTime > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
+ if (period > MAX_SUPPORTED_TIMEOUT)
+ throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
+ Contract.EndContractBlock();
+
+ return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
+ }
+
+ public bool Dispose(WaitHandle notifyObject)
+ {
+ if (notifyObject==null)
+ throw new ArgumentNullException("notifyObject");
+ Contract.EndContractBlock();
+
+ return m_timer.Close(notifyObject);
+ }
+
+ public void Dispose()
+ {
+ m_timer.Close();
+ }
+
+ internal void KeepRootedWhileScheduled()
+ {
+ GC.SuppressFinalize(m_timer);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/Volatile.cs b/src/mscorlib/src/System/Threading/Volatile.cs
new file mode 100644
index 0000000000..af687fbae1
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/Volatile.cs
@@ -0,0 +1,441 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.CompilerServices;
+using System.Runtime;
+using System.Security;
+
+namespace System.Threading
+{
+ //
+ // Methods for accessing memory with volatile semantics. These are preferred over Thread.VolatileRead
+ // and Thread.VolatileWrite, as these are implemented more efficiently.
+ //
+ // (We cannot change the implementations of Thread.VolatileRead/VolatileWrite without breaking code
+ // that relies on their overly-strong ordering guarantees.)
+ //
+ // The actual implementations of these methods are typically supplied by the VM at JIT-time, because C# does
+ // not allow us to express a volatile read/write from/to a byref arg.
+ // See getILIntrinsicImplementationForVolatile() in jitinterface.cpp.
+ //
+ public static class Volatile
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static bool Read(ref bool location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static sbyte Read(ref sbyte location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static byte Read(ref byte location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static short Read(ref short location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static ushort Read(ref ushort location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static int Read(ref int location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static uint Read(ref uint location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+#if BIT64
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static long Read(ref long location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ [SecuritySafeCritical] // to match 32-bit version
+ public static ulong Read(ref ulong location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+#else
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static long Read(ref long location)
+ {
+ //
+ // On 32-bit machines, we use this implementation, since an ordinary volatile read
+ // would not be atomic.
+ //
+ // On 64-bit machines, the VM will replace this with a more efficient implementation.
+ //
+ return Interlocked.CompareExchange(ref location, 0, 0);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [SecuritySafeCritical] // contains unsafe code
+ public static ulong Read(ref ulong location)
+ {
+ unsafe
+ {
+ //
+ // There is no overload of Interlocked.Exchange that accepts a ulong. So we have
+ // to do some pointer tricks to pass our arguments to the overload that takes a long.
+ //
+ fixed (ulong* pLocation = &location)
+ {
+ return (ulong)Interlocked.CompareExchange(ref *(long*)pLocation, 0, 0);
+ }
+ }
+ }
+#endif
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static IntPtr Read(ref IntPtr location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static UIntPtr Read(ref UIntPtr location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static float Read(ref float location)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static double Read(ref double location)
+ {
+ //
+ // On 32-bit machines, we use this implementation, since an ordinary volatile read
+ // would not be atomic.
+ //
+ // On 64-bit machines, the VM will replace this with a more efficient implementation.
+ //
+ return Interlocked.CompareExchange(ref location, 0, 0);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [SecuritySafeCritical] //the intrinsic implementation of this method contains unverifiable code
+ [System.Runtime.Versioning.NonVersionable]
+ public static T Read<T>(ref T location) where T : class
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ var value = location;
+ Thread.MemoryBarrier();
+ return value;
+ }
+
+
+
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref bool location, bool value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref sbyte location, sbyte value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref byte location, byte value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref short location, short value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref ushort location, ushort value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref int location, int value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref uint location, uint value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+#if BIT64
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref long location, long value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ [SecuritySafeCritical] // to match 32-bit version
+ public static void Write(ref ulong location, ulong value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+#else
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static void Write(ref long location, long value)
+ {
+ //
+ // On 32-bit machines, we use this implementation, since an ordinary volatile write
+ // would not be atomic.
+ //
+ // On 64-bit machines, the VM will replace this with a more efficient implementation.
+ //
+ Interlocked.Exchange(ref location, value);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [SecuritySafeCritical] // contains unsafe code
+ public static void Write(ref ulong location, ulong value)
+ {
+ //
+ // On 32-bit machines, we use this implementation, since an ordinary volatile write
+ // would not be atomic.
+ //
+ // On 64-bit machines, the VM will replace this with a more efficient implementation.
+ //
+ unsafe
+ {
+ //
+ // There is no overload of Interlocked.Exchange that accepts a ulong. So we have
+ // to do some pointer tricks to pass our arguments to the overload that takes a long.
+ //
+ fixed (ulong* pLocation = &location)
+ {
+ Interlocked.Exchange(ref *(long*)pLocation, (long)value);
+ }
+ }
+ }
+#endif
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref IntPtr location, IntPtr value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [CLSCompliant(false)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref UIntPtr location, UIntPtr value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref float location, float value)
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write(ref double location, double value)
+ {
+ //
+ // On 32-bit machines, we use this implementation, since an ordinary volatile write
+ // would not be atomic.
+ //
+ // On 64-bit machines, the VM will replace this with a more efficient implementation.
+ //
+ Interlocked.Exchange(ref location, value);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [SecuritySafeCritical] //the intrinsic implementation of this method contains unverifiable code
+ [System.Runtime.Versioning.NonVersionable]
+ public static void Write<T>(ref T location, T value) where T : class
+ {
+ //
+ // The VM will replace this with a more efficient implementation.
+ //
+ Thread.MemoryBarrier();
+ location = value;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/WaitHandle.cs b/src/mscorlib/src/System/Threading/WaitHandle.cs
new file mode 100644
index 0000000000..9980c822a6
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/WaitHandle.cs
@@ -0,0 +1,617 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Class to represent all synchronization objects in the runtime (that allow multiple wait)
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ using System.Threading;
+ using System.Runtime.Remoting;
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+ using Microsoft.Win32.SafeHandles;
+ using System.Runtime.Versioning;
+ using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+ using Win32Native = Microsoft.Win32.Win32Native;
+
+[System.Runtime.InteropServices.ComVisible(true)]
+#if FEATURE_REMOTING
+ public abstract class WaitHandle : MarshalByRefObject, IDisposable {
+#else // FEATURE_REMOTING
+ public abstract class WaitHandle : IDisposable {
+#endif // FEATURE_REMOTING
+ public const int WaitTimeout = 0x102;
+
+ private const int MAX_WAITHANDLES = 64;
+
+#pragma warning disable 414 // Field is not used from managed.
+ private IntPtr waitHandle; // !!! DO NOT MOVE THIS FIELD. (See defn of WAITHANDLEREF in object.h - has hardcoded access to this field.)
+#pragma warning restore 414
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal volatile SafeWaitHandle safeWaitHandle;
+
+ internal bool hasThreadAffinity;
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static IntPtr GetInvalidHandle()
+ {
+ return Win32Native.INVALID_HANDLE_VALUE;
+ }
+ protected static readonly IntPtr InvalidHandle = GetInvalidHandle();
+ private const int WAIT_OBJECT_0 = 0;
+ private const int WAIT_ABANDONED = 0x80;
+ private const int WAIT_FAILED = 0x7FFFFFFF;
+ private const int ERROR_TOO_MANY_POSTS = 0x12A;
+
+ internal enum OpenExistingResult
+ {
+ Success,
+ NameNotFound,
+ PathNotFound,
+ NameInvalid
+ }
+
+ protected WaitHandle()
+ {
+ Init();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private void Init()
+ {
+ safeWaitHandle = null;
+ waitHandle = InvalidHandle;
+ hasThreadAffinity = false;
+ }
+
+
+ [Obsolete("Use the SafeWaitHandle property instead.")]
+ public virtual IntPtr Handle
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get { return safeWaitHandle == null ? InvalidHandle : safeWaitHandle.DangerousGetHandle();}
+
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ set
+ {
+ if (value == InvalidHandle)
+ {
+ // This line leaks a handle. However, it's currently
+ // not perfectly clear what the right behavior is here
+ // anyways. This preserves Everett behavior. We should
+ // ideally do these things:
+ // *) Expose a settable SafeHandle property on WaitHandle.
+ // *) Expose a settable OwnsHandle property on SafeHandle.
+ if (safeWaitHandle != null)
+ {
+ safeWaitHandle.SetHandleAsInvalid();
+ safeWaitHandle = null;
+ }
+ }
+ else
+ {
+ safeWaitHandle = new SafeWaitHandle(value, true);
+ }
+ waitHandle = value;
+ }
+ }
+
+
+ public SafeWaitHandle SafeWaitHandle
+ {
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ get
+ {
+ if (safeWaitHandle == null)
+ {
+ safeWaitHandle = new SafeWaitHandle(InvalidHandle, false);
+ }
+ return safeWaitHandle;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ set
+ {
+ // Set safeWaitHandle and waitHandle in a CER so we won't take
+ // a thread abort between the statements and leave the wait
+ // handle in an invalid state. Note this routine is not thread
+ // safe however.
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try { }
+ finally
+ {
+ if (value == null)
+ {
+ safeWaitHandle = null;
+ waitHandle = InvalidHandle;
+ }
+ else
+ {
+ safeWaitHandle = value;
+ waitHandle = safeWaitHandle.DangerousGetHandle();
+ }
+ }
+ }
+ }
+
+ // Assembly-private version that doesn't do a security check. Reduces the
+ // number of link-time security checks when reading & writing to a file,
+ // and helps avoid a link time check while initializing security (If you
+ // call a Serialization method that requires security before security
+ // has started up, the link time check will start up security, run
+ // serialization code for some security attribute stuff, call into
+ // FileStream, which will then call Sethandle, which requires a link time
+ // security check.). While security has fixed that problem, we still
+ // don't need to do a linktime check here.
+ [System.Security.SecurityCritical] // auto-generated
+ internal void SetHandleInternal(SafeWaitHandle handle)
+ {
+ safeWaitHandle = handle;
+ waitHandle = handle.DangerousGetHandle();
+ }
+
+ public virtual bool WaitOne (int millisecondsTimeout, bool exitContext)
+ {
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ return WaitOne((long)millisecondsTimeout,exitContext);
+ }
+
+ public virtual bool WaitOne (TimeSpan timeout, bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitOne(tm,exitContext);
+ }
+
+ public virtual bool WaitOne ()
+ {
+ //Infinite Timeout
+ return WaitOne(-1,false);
+ }
+
+ public virtual bool WaitOne(int millisecondsTimeout)
+ {
+ return WaitOne(millisecondsTimeout, false);
+ }
+
+ public virtual bool WaitOne(TimeSpan timeout)
+ {
+ return WaitOne(timeout, false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
+ private bool WaitOne(long timeout, bool exitContext)
+ {
+ return InternalWaitOne(safeWaitHandle, timeout, hasThreadAffinity, exitContext);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal static bool InternalWaitOne(SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
+ {
+ if (waitableSafeHandle == null)
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
+ }
+ Contract.EndContractBlock();
+ int ret = WaitOneNative(waitableSafeHandle, (uint)millisecondsTimeout, hasThreadAffinity, exitContext);
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if (ret == WAIT_ABANDONED)
+ {
+ ThrowAbandonedMutexException();
+ }
+ return (ret != WaitTimeout);
+ }
+
+ [System.Security.SecurityCritical]
+ internal bool WaitOneWithoutFAS()
+ {
+ // version of waitone without fast application switch (FAS) support
+ // This is required to support the Wait which FAS needs (otherwise recursive dependency comes in)
+ if (safeWaitHandle == null)
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
+ }
+ Contract.EndContractBlock();
+
+ long timeout = -1;
+ int ret = WaitOneNative(safeWaitHandle, (uint)timeout, hasThreadAffinity, false);
+ if (ret == WAIT_ABANDONED)
+ {
+ ThrowAbandonedMutexException();
+ }
+ return (ret != WaitTimeout);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);
+
+ /*========================================================================
+ ** Waits for signal from all the objects.
+ ** timeout indicates how long to wait before the method returns.
+ ** This method will return either when all the object have been pulsed
+ ** or timeout milliseonds have elapsed.
+ ** If exitContext is true then the synchronization domain for the context
+ ** (if in a synchronized context) is exited before the wait and reacquired
+ ========================================================================*/
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private static extern int WaitMultiple(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext, bool WaitAll);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+ {
+ if (waitHandles == null)
+ {
+ throw new ArgumentNullException("waitHandles", Environment.GetResourceString("ArgumentNull_Waithandles"));
+ }
+ if(waitHandles.Length == 0)
+ {
+ //
+ // Some history: in CLR 1.0 and 1.1, we threw ArgumentException in this case, which was correct.
+ // Somehow, in 2.0, this became ArgumentNullException. This was not fixed until Silverlight 2,
+ // which went back to ArgumentException.
+ //
+ // Now we're in a bit of a bind. Backward-compatibility requires us to keep throwing ArgumentException
+ // in CoreCLR, and ArgumentNullException in the desktop CLR. This is ugly, but so is breaking
+ // user code.
+ //
+#if FEATURE_CORECLR
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+#else
+ throw new ArgumentNullException("waitHandles", Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+#endif
+ }
+ if (waitHandles.Length > MAX_WAITHANDLES)
+ {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles"));
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length];
+ for (int i = 0; i < waitHandles.Length; i ++)
+ {
+ WaitHandle waitHandle = waitHandles[i];
+
+ if (waitHandle == null)
+ throw new ArgumentNullException("waitHandles[" + i + "]", Environment.GetResourceString("ArgumentNull_ArrayElement"));
+
+#if FEATURE_REMOTING
+ if (RemotingServices.IsTransparentProxy(waitHandle))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
+#endif
+
+ internalWaitHandles[i] = waitHandle;
+ }
+#if _DEBUG
+ // make sure we do not use waitHandles any more.
+ waitHandles = null;
+#endif
+
+ int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, true /* waitall*/ );
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret))
+ {
+ //In the case of WaitAll the OS will only provide the
+ // information that mutex was abandoned.
+ // It won't tell us which one. So we can't set the Index or provide access to the Mutex
+ ThrowAbandonedMutexException();
+ }
+
+ GC.KeepAlive(internalWaitHandles);
+ return (ret != WaitTimeout);
+ }
+
+ public static bool WaitAll(
+ WaitHandle[] waitHandles,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitAll(waitHandles,(int)tm, exitContext);
+ }
+
+
+ /*========================================================================
+ ** Shorthand for WaitAll with timeout = Timeout.Infinite and exitContext = true
+ ========================================================================*/
+ public static bool WaitAll(WaitHandle[] waitHandles)
+ {
+ return WaitAll(waitHandles, Timeout.Infinite, true);
+ }
+
+ public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout)
+ {
+ return WaitAll(waitHandles, millisecondsTimeout, true);
+ }
+
+ public static bool WaitAll(WaitHandle[] waitHandles, TimeSpan timeout)
+ {
+ return WaitAll(waitHandles, timeout, true);
+ }
+
+
+ /*========================================================================
+ ** Waits for notification from any of the objects.
+ ** timeout indicates how long to wait before the method returns.
+ ** This method will return either when either one of the object have been
+ ** signalled or timeout milliseonds have elapsed.
+ ** If exitContext is true then the synchronization domain for the context
+ ** (if in a synchronized context) is exited before the wait and reacquired
+ ========================================================================*/
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+ {
+ if (waitHandles==null)
+ {
+ throw new ArgumentNullException("waitHandles", Environment.GetResourceString("ArgumentNull_Waithandles"));
+ }
+ if(waitHandles.Length == 0)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+ }
+ if (MAX_WAITHANDLES < waitHandles.Length)
+ {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles"));
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length];
+ for (int i = 0; i < waitHandles.Length; i ++)
+ {
+ WaitHandle waitHandle = waitHandles[i];
+
+ if (waitHandle == null)
+ throw new ArgumentNullException("waitHandles[" + i + "]", Environment.GetResourceString("ArgumentNull_ArrayElement"));
+
+#if FEATURE_REMOTING
+ if (RemotingServices.IsTransparentProxy(waitHandle))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
+#endif
+
+ internalWaitHandles[i] = waitHandle;
+ }
+#if _DEBUG
+ // make sure we do not use waitHandles any more.
+ waitHandles = null;
+#endif
+ int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, false /* waitany*/ );
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret))
+ {
+ int mutexIndex = ret -WAIT_ABANDONED;
+ if(0 <= mutexIndex && mutexIndex < internalWaitHandles.Length)
+ {
+ ThrowAbandonedMutexException(mutexIndex,internalWaitHandles[mutexIndex]);
+ }
+ else
+ {
+ ThrowAbandonedMutexException();
+ }
+ }
+
+ GC.KeepAlive(internalWaitHandles);
+ return ret;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(
+ WaitHandle[] waitHandles,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitAny(waitHandles,(int)tm, exitContext);
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout)
+ {
+ return WaitAny(waitHandles, timeout, true);
+ }
+
+
+ /*========================================================================
+ ** Shorthand for WaitAny with timeout = Timeout.Infinite and exitContext = true
+ ========================================================================*/
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles)
+ {
+ return WaitAny(waitHandles, Timeout.Infinite, true);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout)
+ {
+ return WaitAny(waitHandles, millisecondsTimeout, true);
+ }
+
+ /*=================================================
+ ==
+ == SignalAndWait
+ ==
+ ==================================================*/
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal,SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout,
+ bool hasThreadAffinity, bool exitContext);
+
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException();
+#else
+ return SignalAndWait(toSignal,toWaitOn,-1,false);
+#endif
+ }
+
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException();
+#else
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return SignalAndWait(toSignal,toWaitOn,(int)tm,exitContext);
+#endif
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn,
+ int millisecondsTimeout,
+ bool exitContext)
+ {
+#if PLATFORM_UNIX
+ throw new PlatformNotSupportedException();
+#else
+ if(null == toSignal)
+ {
+ throw new ArgumentNullException("toSignal");
+ }
+ if(null == toWaitOn)
+ {
+ throw new ArgumentNullException("toWaitOn");
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+
+ //NOTE: This API is not supporting Pause/Resume as it's not exposed in CoreCLR (not in WP or SL)
+ int ret = SignalAndWaitOne(toSignal.safeWaitHandle,toWaitOn.safeWaitHandle,millisecondsTimeout,
+ toWaitOn.hasThreadAffinity,exitContext);
+
+#if !FEATURE_CORECLR
+ if(WAIT_FAILED != ret && toSignal.hasThreadAffinity)
+ {
+ Thread.EndCriticalRegion();
+ Thread.EndThreadAffinity();
+ }
+#endif
+
+ if(WAIT_ABANDONED == ret)
+ {
+ ThrowAbandonedMutexException();
+ }
+
+ if(ERROR_TOO_MANY_POSTS == ret)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Threading.WaitHandleTooManyPosts"));
+ }
+
+ //Object was signaled
+ if(WAIT_OBJECT_0 == ret)
+ {
+ return true;
+ }
+
+ //Timeout
+ return false;
+#endif
+ }
+
+ private static void ThrowAbandonedMutexException()
+ {
+ throw new AbandonedMutexException();
+ }
+
+ private static void ThrowAbandonedMutexException(int location, WaitHandle handle)
+ {
+ throw new AbandonedMutexException(location, handle);
+ }
+
+ public virtual void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ protected virtual void Dispose(bool explicitDisposing)
+ {
+ if (safeWaitHandle != null)
+ {
+ safeWaitHandle.Close();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs
new file mode 100644
index 0000000000..f873057992
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+namespace System.Threading
+{
+ using System;
+ using System.Runtime.Serialization;
+ using System.Runtime.InteropServices;
+
+ [Serializable]
+ [ComVisibleAttribute(false)]
+
+#if FEATURE_CORECLR
+ public class WaitHandleCannotBeOpenedException : Exception {
+#else
+ public class WaitHandleCannotBeOpenedException : ApplicationException {
+#endif // FEATURE_CORECLR
+ public WaitHandleCannotBeOpenedException() : base(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException"))
+ {
+ SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED);
+ }
+
+ public WaitHandleCannotBeOpenedException(String message) : base(message)
+ {
+ SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED);
+ }
+
+ public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException)
+ {
+ SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED);
+ }
+
+ protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base (info, context)
+ {
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Threading/WaitHandleExtensions.cs b/src/mscorlib/src/System/Threading/WaitHandleExtensions.cs
new file mode 100644
index 0000000000..76c3feb649
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/WaitHandleExtensions.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+using Microsoft.Win32.SafeHandles;
+using System.Security;
+
+namespace System.Threading
+{
+ public static class WaitHandleExtensions
+ {
+ /// <summary>
+ /// Gets the native operating system handle.
+ /// </summary>
+ /// <param name="waitHandle">The <see cref="System.Threading.WaitHandle"/> to operate on.</param>
+ /// <returns>A <see cref="System.Runtime.InteropServices.SafeHandle"/> representing the native operating system handle.</returns>
+ [SecurityCritical]
+ public static SafeWaitHandle GetSafeWaitHandle(this WaitHandle waitHandle)
+ {
+ if (waitHandle == null)
+ {
+ throw new ArgumentNullException("waitHandle");
+ }
+
+ return waitHandle.SafeWaitHandle;
+ }
+
+ /// <summary>
+ /// Sets the native operating system handle
+ /// </summary>
+ /// <param name="waitHandle">The <see cref="System.Threading.WaitHandle"/> to operate on.</param>
+ /// <param name="value">A <see cref="System.Runtime.InteropServices.SafeHandle"/> representing the native operating system handle.</param>
+ [SecurityCritical]
+ public static void SetSafeWaitHandle(this WaitHandle waitHandle, SafeWaitHandle value)
+ {
+ if (waitHandle == null)
+ {
+ throw new ArgumentNullException("waitHandle");
+ }
+
+ waitHandle.SafeWaitHandle = value;
+ }
+ }
+}