diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading')
55 files changed, 752 insertions, 8214 deletions
diff --git a/src/mscorlib/src/System/Threading/ApartmentState.cs b/src/mscorlib/src/System/Threading/ApartmentState.cs index 844f85e9e7..1edf0af98a 100644 --- a/src/mscorlib/src/System/Threading/ApartmentState.cs +++ b/src/mscorlib/src/System/Threading/ApartmentState.cs @@ -15,7 +15,6 @@ namespace System.Threading { [Serializable] -[System.Runtime.InteropServices.ComVisible(true)] public enum ApartmentState { /*========================================================================= diff --git a/src/mscorlib/src/System/Threading/AsyncLocal.cs b/src/mscorlib/src/System/Threading/AsyncLocal.cs index 6ed1545ac7..8c4319bd2c 100644 --- a/src/mscorlib/src/System/Threading/AsyncLocal.cs +++ b/src/mscorlib/src/System/Threading/AsyncLocal.cs @@ -127,8 +127,6 @@ namespace System.Threading { public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap(); - public static IAsyncLocalValueMap Create(IAsyncLocal key, object value) => new OneElementAsyncLocalValueMap(key, value); - // Instance without any key/value pairs. Used as a singleton/ private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap { diff --git a/src/mscorlib/src/System/Threading/AutoResetEvent.cs b/src/mscorlib/src/System/Threading/AutoResetEvent.cs index 78a6fa1234..fc6b2301ca 100644 --- a/src/mscorlib/src/System/Threading/AutoResetEvent.cs +++ b/src/mscorlib/src/System/Threading/AutoResetEvent.cs @@ -14,10 +14,8 @@ namespace System.Threading { using System; - using System.Security.Permissions; using System.Runtime.InteropServices; - [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 index 5b78f20900..b68ba4c046 100644 --- a/src/mscorlib/src/System/Threading/CancellationToken.cs +++ b/src/mscorlib/src/System/Threading/CancellationToken.cs @@ -9,7 +9,6 @@ using System; using System.Runtime.InteropServices; -using System.Security.Permissions; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime; @@ -38,7 +37,6 @@ namespace System.Threading /// All members of this struct are thread-safe and may be used concurrently from multiple threads. /// </para> /// </remarks> - [ComVisible(false)] [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")] public struct CancellationToken { @@ -317,11 +315,8 @@ namespace System.Threading } // the real work.. - [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(nameof(callback)); @@ -341,8 +336,7 @@ namespace System.Threading 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 + capturedExecutionContext = ExecutionContext.Capture(); } // Register the callback with the source. diff --git a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs index ac27fe30e6..89e98fa3c8 100644 --- a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs +++ b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs @@ -6,7 +6,6 @@ //////////////////////////////////////////////////////////////////////////////// using System.Diagnostics.Contracts; -using System.Security.Permissions; using System.Runtime.CompilerServices; namespace System.Threading diff --git a/src/mscorlib/src/System/Threading/CancellationTokenSource.cs b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs index fe9e0dec76..1e70d6f30f 100644 --- a/src/mscorlib/src/System/Threading/CancellationTokenSource.cs +++ b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs @@ -10,7 +10,6 @@ using System; using System.Security; using System.Collections.Generic; using System.Runtime.InteropServices; -using System.Security.Permissions; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime; @@ -35,7 +34,6 @@ namespace System.Threading /// concurrently from multiple threads. /// </para> /// </remarks> - [ComVisible(false)] public class CancellationTokenSource : IDisposable { @@ -864,12 +862,19 @@ namespace System.Threading /// <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(); - } + public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) => + !token1.CanBeCanceled ? CreateLinkedTokenSource(token2) : + token2.CanBeCanceled ? new Linked2CancellationTokenSource(token1, token2) : + (CancellationTokenSource)new Linked1CancellationTokenSource(token1); + + /// <summary> + /// Creates a <see cref="CancellationTokenSource"/> that will be in the canceled state + /// when any of the source tokens are in the canceled state. + /// </summary> + /// <param name="token">The first <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to observe.</param> + /// <returns>A <see cref="CancellationTokenSource"/> that is linked to the source tokens.</returns> + internal static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token) => + token.CanBeCanceled ? new Linked1CancellationTokenSource(token) : new CancellationTokenSource(); /// <summary> /// Creates a <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that will be in the canceled state @@ -884,14 +889,19 @@ namespace System.Threading if (tokens == null) throw new ArgumentNullException(nameof(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); + switch (tokens.Length) + { + case 0: + throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty")); + case 1: + return CreateLinkedTokenSource(tokens[0]); + case 2: + return CreateLinkedTokenSource(tokens[0], tokens[1]); + default: + // a defensive copy is not required as the array has value-items that have only a single reference field, + // hence each item cannot be null itself, and reads of the payloads cannot be torn. + return new LinkedNCancellationTokenSource(tokens); + } } @@ -907,35 +917,50 @@ namespace System.Threading } } - private sealed class LinkedCancellationTokenSource : CancellationTokenSource + private sealed class Linked1CancellationTokenSource : CancellationTokenSource { - private static readonly Action<object> s_linkedTokenCancelDelegate = - s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel() - private CancellationTokenRegistration[] m_linkingRegistrations; + private readonly CancellationTokenRegistration _reg1; - internal LinkedCancellationTokenSource(CancellationToken token1, CancellationToken token2) + internal Linked1CancellationTokenSource(CancellationToken token1) { - bool token2CanBeCanceled = token2.CanBeCanceled; + _reg1 = token1.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); + } - 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); - } + protected override void Dispose(bool disposing) + { + if (!disposing || m_disposed) return; + _reg1.Dispose(); + base.Dispose(disposing); + } + } - 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); - } + private sealed class Linked2CancellationTokenSource : CancellationTokenSource + { + private readonly CancellationTokenRegistration _reg1; + private readonly CancellationTokenRegistration _reg2; + + internal Linked2CancellationTokenSource(CancellationToken token1, CancellationToken token2) + { + _reg1 = token1.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); + _reg2 = token2.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); } - internal LinkedCancellationTokenSource(params CancellationToken[] tokens) + protected override void Dispose(bool disposing) + { + if (!disposing || m_disposed) return; + _reg1.Dispose(); + _reg2.Dispose(); + base.Dispose(disposing); + } + } + + private sealed class LinkedNCancellationTokenSource : CancellationTokenSource + { + internal static readonly Action<object> s_linkedTokenCancelDelegate = + s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel() + private CancellationTokenRegistration[] m_linkingRegistrations; + + internal LinkedNCancellationTokenSource(params CancellationToken[] tokens) { m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; @@ -968,7 +993,6 @@ namespace System.Threading base.Dispose(disposing); } - } } diff --git a/src/mscorlib/src/System/Threading/CountdownEvent.cs b/src/mscorlib/src/System/Threading/CountdownEvent.cs index d86a2ccfb8..af055e347e 100644 --- a/src/mscorlib/src/System/Threading/CountdownEvent.cs +++ b/src/mscorlib/src/System/Threading/CountdownEvent.cs @@ -11,7 +11,6 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; -using System.Security.Permissions; using System.Runtime.InteropServices; using System.Threading; using System.Diagnostics; @@ -30,7 +29,6 @@ namespace System.Threading /// 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}")] public class CountdownEvent : IDisposable { diff --git a/src/mscorlib/src/System/Threading/EventWaitHandle.cs b/src/mscorlib/src/System/Threading/EventWaitHandle.cs index 4b1611c6aa..0268948a5c 100644 --- a/src/mscorlib/src/System/Threading/EventWaitHandle.cs +++ b/src/mscorlib/src/System/Threading/EventWaitHandle.cs @@ -15,10 +15,10 @@ namespace System.Security.AccessControl { - public class EventWaitHandleSecurity + internal class EventWaitHandleSecurity { } - public enum EventWaitHandleRights + internal enum EventWaitHandleRights { } } @@ -28,7 +28,6 @@ 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; @@ -89,7 +88,7 @@ namespace System.Threading { } - public unsafe EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity) + internal unsafe EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity) { if(name != null) { @@ -146,7 +145,7 @@ namespace System.Threading return OpenExisting(name, (EventWaitHandleRights)0); } - public static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights) + internal static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights) { EventWaitHandle result; switch (OpenExistingWorker(name, rights, out result)) @@ -171,11 +170,6 @@ namespace System.Threading return OpenExistingWorker(name, (EventWaitHandleRights)0, out result) == OpenExistingResult.Success; } - public static bool TryOpenExisting(string name, EventWaitHandleRights rights, out EventWaitHandle result) - { - return OpenExistingWorker(name, rights, out result) == OpenExistingResult.Success; - } - private static OpenExistingResult OpenExistingWorker(string name, EventWaitHandleRights rights, out EventWaitHandle result) { #if PLATFORM_UNIX diff --git a/src/mscorlib/src/System/Threading/ExecutionContext.cs b/src/mscorlib/src/System/Threading/ExecutionContext.cs index 5ea9942f65..47a55a3bb9 100644 --- a/src/mscorlib/src/System/Threading/ExecutionContext.cs +++ b/src/mscorlib/src/System/Threading/ExecutionContext.cs @@ -19,7 +19,6 @@ namespace System.Threading using System.Reflection; using System.Runtime.ExceptionServices; using System.Runtime.Serialization; - using System.Security.Permissions; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; @@ -27,7 +26,6 @@ namespace System.Threading using System.Diagnostics.Contracts; using System.Diagnostics.CodeAnalysis; - [System.Runtime.InteropServices.ComVisible(true)] public delegate void ContextCallback(Object state); internal struct ExecutionContextSwitcher @@ -55,7 +53,7 @@ namespace System.Threading [Serializable] public sealed class ExecutionContext : IDisposable, ISerializable { - private static readonly ExecutionContext Default = new ExecutionContext(); + internal static readonly ExecutionContext Default = new ExecutionContext(); private readonly IAsyncLocalValueMap m_localValues; private readonly IAsyncLocal[] m_localChangeNotifications; @@ -93,16 +91,10 @@ namespace System.Threading public static ExecutionContext Capture() { ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext; - if (executionContext == null) - { - return Default; - } - if (executionContext.m_isFlowSuppressed) - { - // Prevent ExecutionContext.Run on a suppressed-flow context for desktop framework compatibility - return null; - } - return executionContext; + return + executionContext == null ? Default : + executionContext.m_isFlowSuppressed ? null : + executionContext; } private ExecutionContext ShallowClone(bool isFlowSuppressed) @@ -301,38 +293,6 @@ namespace System.Threading } } - #region Wrappers for CLR compat, to avoid ifdefs all over the BCL - - [Flags] - internal enum CaptureOptions - { - None = 0x00, - IgnoreSyncCtx = 0x01, - OptimizeDefaultCase = 0x02, - } - - internal static ExecutionContext Capture(ref StackCrawlMark stackMark, CaptureOptions captureOptions) - { - return Capture(); - } - - [FriendAccessAllowed] - internal static ExecutionContext FastCapture() - { - return Capture(); - } - - [FriendAccessAllowed] - internal static void Run(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx) - { - Run(executionContext, callback, state); - } - - internal bool IsDefaultFTContext(bool ignoreSyncCtx) - { - return this == Default; - } - public ExecutionContext CreateCopy() { return this; // since CoreCLR's ExecutionContext is immutable, we don't need to create copies. @@ -342,18 +302,6 @@ namespace System.Threading { // For CLR compat only } - - internal static ExecutionContext PreAllocatedDefault - { - get { return ExecutionContext.Default; } - } - - internal bool IsPreAllocatedDefault - { - get { return this == ExecutionContext.Default; } - } - - #endregion } public struct AsyncFlowControl : IDisposable diff --git a/src/mscorlib/src/System/Threading/IObjectHandle.cs b/src/mscorlib/src/System/Threading/IObjectHandle.cs deleted file mode 100644 index 464f06e52d..0000000000 --- a/src/mscorlib/src/System/Threading/IObjectHandle.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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 index 8a0b527fc0..131d51a65b 100644 --- a/src/mscorlib/src/System/Threading/Interlocked.cs +++ b/src/mscorlib/src/System/Threading/Interlocked.cs @@ -6,7 +6,6 @@ namespace System.Threading { using System; - using System.Security.Permissions; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.Versioning; @@ -25,13 +24,11 @@ namespace System.Threading * 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); @@ -43,7 +40,6 @@ namespace System.Threading * long *****************************/ - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static int Decrement(ref int location) { return Add(ref location, -1); @@ -65,7 +61,6 @@ namespace System.Threading *****************************/ [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern int Exchange(ref int location1, int value); [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -78,15 +73,11 @@ namespace System.Threading public static extern double Exchange(ref double location1, double value); [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern Object Exchange(ref Object location1, Object value); [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern IntPtr Exchange(ref IntPtr location1, IntPtr value); - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - [System.Runtime.InteropServices.ComVisible(false)] public static T Exchange<T>(ref T location1, T value) where T : class { _Exchange(__makeref(location1), __makeref(value)); @@ -98,7 +89,6 @@ namespace System.Threading } [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern void _Exchange(TypedReference location1, TypedReference value); /****************************** @@ -112,7 +102,6 @@ namespace System.Threading *****************************/ [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern int CompareExchange(ref int location1, int value, int comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -125,11 +114,9 @@ namespace System.Threading public static extern double CompareExchange(ref double location1, double value, double comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern Object CompareExchange(ref Object location1, Object value, Object comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand); /***************************************************************** @@ -156,8 +143,6 @@ namespace System.Threading * for details. *****************************************************************/ - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - [System.Runtime.InteropServices.ComVisible(false)] 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' @@ -166,12 +151,10 @@ namespace System.Threading } [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 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)] internal static extern int CompareExchange(ref int location1, int value, int comparand, ref bool succeeded); /****************************** @@ -181,19 +164,16 @@ namespace System.Threading *****************************/ [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; diff --git a/src/mscorlib/src/System/Threading/LazyInitializer.cs b/src/mscorlib/src/System/Threading/LazyInitializer.cs index 238cc89dbd..af32673d03 100644 --- a/src/mscorlib/src/System/Threading/LazyInitializer.cs +++ b/src/mscorlib/src/System/Threading/LazyInitializer.cs @@ -11,7 +11,6 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -using System.Security.Permissions; using System.Diagnostics; using System.Diagnostics.Contracts; namespace System.Threading diff --git a/src/mscorlib/src/System/Threading/LockCookie.cs b/src/mscorlib/src/System/Threading/LockCookie.cs deleted file mode 100644 index c1fbfd828e..0000000000 --- a/src/mscorlib/src/System/Threading/LockCookie.cs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 index ab95e70c7e..40f04b00b4 100644 --- a/src/mscorlib/src/System/Threading/LockRecursionException.cs +++ b/src/mscorlib/src/System/Threading/LockRecursionException.cs @@ -19,7 +19,6 @@ namespace System.Threading using System.Runtime.CompilerServices; [Serializable] - [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] public class LockRecursionException : System.Exception { public LockRecursionException() { } diff --git a/src/mscorlib/src/System/Threading/ManualResetEvent.cs b/src/mscorlib/src/System/Threading/ManualResetEvent.cs index 00041567df..a8e012fb43 100644 --- a/src/mscorlib/src/System/Threading/ManualResetEvent.cs +++ b/src/mscorlib/src/System/Threading/ManualResetEvent.cs @@ -14,10 +14,8 @@ namespace System.Threading { using System; - using System.Security.Permissions; using System.Runtime.InteropServices; -[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 index 509af5bfa0..2d57b4102b 100644 --- a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs +++ b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs @@ -13,7 +13,6 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; -using System.Security.Permissions; using System.Threading; using System.Runtime.InteropServices; using System.Diagnostics; @@ -45,7 +44,6 @@ namespace System.Threading /// completed, and Reset, which should only be used when no other threads are /// accessing the event. /// </remarks> - [ComVisible(false)] [DebuggerDisplay("Set = {IsSet}")] public class ManualResetEventSlim : IDisposable { diff --git a/src/mscorlib/src/System/Threading/Monitor.cs b/src/mscorlib/src/System/Threading/Monitor.cs index 94dec13d05..fdbc384243 100644 --- a/src/mscorlib/src/System/Threading/Monitor.cs +++ b/src/mscorlib/src/System/Threading/Monitor.cs @@ -17,7 +17,6 @@ namespace System.Threading { using System; - using System.Security.Permissions; using System.Runtime; using System.Runtime.Remoting; using System.Threading; @@ -27,7 +26,6 @@ namespace System.Threading { using System.Diagnostics; using System.Diagnostics.Contracts; - [System.Runtime.InteropServices.ComVisible(true)] public static class Monitor { /*========================================================================= @@ -76,7 +74,6 @@ namespace System.Threading { ** own the lock. =========================================================================*/ [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static extern void Exit(Object obj); /*========================================================================= diff --git a/src/mscorlib/src/System/Threading/Mutex.cs b/src/mscorlib/src/System/Threading/Mutex.cs index 506abb7a07..dcb821307a 100644 --- a/src/mscorlib/src/System/Threading/Mutex.cs +++ b/src/mscorlib/src/System/Threading/Mutex.cs @@ -16,7 +16,6 @@ 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; @@ -27,23 +26,20 @@ namespace System.Threading using System.Diagnostics; using System.Diagnostics.Contracts; - [ComVisible(true)] public sealed class Mutex : WaitHandle { static bool dummyBool; - public class MutexSecurity + internal class MutexSecurity { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Mutex(bool initiallyOwned, String name, out bool createdNew) : this(initiallyOwned, name, out createdNew, (MutexSecurity)null) { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - public unsafe Mutex(bool initiallyOwned, String name, out bool createdNew, MutexSecurity mutexSecurity) + internal unsafe Mutex(bool initiallyOwned, String name, out bool createdNew, MutexSecurity mutexSecurity) { if (name == string.Empty) { @@ -62,26 +58,6 @@ namespace System.Threading CreateMutexWithGuaranteedCleanup(initiallyOwned, name, out createdNew, secAttrs); } - [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), nameof(name)); - } -#endif - Contract.EndContractBlock(); - - CreateMutexWithGuaranteedCleanup(initiallyOwned, name, out createdNew, secAttrs); - } - - [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); @@ -104,7 +80,6 @@ namespace System.Threading Win32Native.SECURITY_ATTRIBUTES m_secAttrs; Mutex m_mutex; - [PrePrepareMethod] internal MutexTryCodeHelper(bool initiallyOwned,MutexCleanupInfo cleanupInfo, String name, Win32Native.SECURITY_ATTRIBUTES secAttrs, Mutex mutex) { Debug.Assert(name == null || name.Length != 0); @@ -116,7 +91,6 @@ namespace System.Threading m_mutex = mutex; } - [PrePrepareMethod] internal void MutexTryCode(object userData) { SafeWaitHandle mutexHandle = null; @@ -153,7 +127,7 @@ namespace System.Threading #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", PathInternal.MaxComponentLength), "name"); + throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), "name"); #endif case Win32Native.ERROR_INVALID_HANDLE: @@ -170,7 +144,6 @@ namespace System.Threading } } - [PrePrepareMethod] private void MutexCleanupCode(Object userData, bool exceptionThrown) { MutexCleanupInfo cleanupInfo = (MutexCleanupInfo) userData; @@ -198,21 +171,17 @@ namespace System.Threading } } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Mutex(bool initiallyOwned, String name) : this(initiallyOwned, name, out dummyBool) { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Mutex(bool initiallyOwned) : this(initiallyOwned, null, out dummyBool) { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Mutex() : this(false, null, out dummyBool) { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private Mutex(SafeWaitHandle handle) { SetHandleInternal(handle); @@ -224,11 +193,11 @@ namespace System.Threading return OpenExisting(name, (MutexRights) 0); } - public enum MutexRights + internal enum MutexRights { } - public static Mutex OpenExisting(string name, MutexRights rights) + internal static Mutex OpenExisting(string name, MutexRights rights) { Mutex result; switch (OpenExistingWorker(name, rights, out result)) @@ -253,11 +222,6 @@ namespace System.Threading return OpenExistingWorker(name, (MutexRights)0, out result) == OpenExistingResult.Success; } - public static bool TryOpenExisting(string name, MutexRights rights, out Mutex result) - { - return OpenExistingWorker(name, rights, out result) == OpenExistingResult.Success; - } - private static OpenExistingResult OpenExistingWorker(string name, MutexRights rights, out Mutex result) { if (name == null) @@ -294,7 +258,7 @@ namespace System.Threading 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", PathInternal.MaxComponentLength), nameof(name)); + throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), nameof(name)); } #endif @@ -316,7 +280,6 @@ namespace System.Threading // 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). - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public void ReleaseMutex() { if (Win32Native.ReleaseMutex(safeWaitHandle)) @@ -324,11 +287,10 @@ namespace System.Threading } else { - throw new Exception(Environment.GetResourceString("Arg_SynchronizationLockException")); + throw new ApplicationException(Environment.GetResourceString("Arg_SynchronizationLockException")); } } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] static int CreateMutexHandle(bool initiallyOwned, String name, Win32Native.SECURITY_ATTRIBUTES securityAttribute, out SafeWaitHandle mutexHandle) { int errorCode; diff --git a/src/mscorlib/src/System/Threading/Overlapped.cs b/src/mscorlib/src/System/Threading/Overlapped.cs index 2b192c7b3a..d3caff5e74 100644 --- a/src/mscorlib/src/System/Threading/Overlapped.cs +++ b/src/mscorlib/src/System/Threading/Overlapped.cs @@ -31,7 +31,6 @@ namespace System.Threading using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; - using System.Security.Permissions; using System.Runtime.ConstrainedExecution; using System.Diagnostics; using System.Diagnostics.Contracts; @@ -44,7 +43,6 @@ namespace System.Threading // 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; @@ -71,13 +69,11 @@ namespace System.Threading { } - internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback, ref StackCrawlMark stackMark) + internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback) { _ioCompletionCallback = ioCompletionCallback; // clone the exection context - _executionContext = ExecutionContext.Capture( - ref stackMark, - ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase); + _executionContext = ExecutionContext.Capture(); } // Context callback: same sig for SendOrPostCallback and ContextCallback static internal ContextCallback _ccb = new ContextCallback(IOCompletionCallback_Context); @@ -103,26 +99,23 @@ namespace System.Threading 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. - + if (helper == null || helper._executionContext == null || helper._executionContext == ExecutionContext.Default) + { + // 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; + ExecutionContext.Run(helper._executionContext, _ccb, helper); + } + + //Quickly check the VM again, to see if a packet has arrived. OverlappedData.CheckVMForIOPacket(out pOVERLAP, out errorCode, out numBytes); - } while (pOVERLAP != null); } @@ -174,17 +167,15 @@ namespace System.Threading m_nativeOverlapped.InternalHigh = (IntPtr)0; } - [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_iocbHelper = new _IOCompletionCallback(iocb); m_iocb = iocb; } else @@ -229,7 +220,6 @@ namespace System.Threading return AllocateNativeOverlapped(); } - [ComVisible(false)] internal IntPtr UserHandle { get { return m_nativeOverlapped.EventHandle; } @@ -255,7 +245,6 @@ namespace System.Threading #region class Overlapped /// <internalonly/> - [System.Runtime.InteropServices.ComVisible(true)] public class Overlapped { private OverlappedData m_overlappedData; @@ -307,7 +296,6 @@ namespace System.Threading set { m_overlappedData.UserHandle = new IntPtr(value); } } - [ComVisible(false)] public IntPtr EventHandleIntPtr { get { return m_overlappedData.UserHandle; } @@ -336,7 +324,7 @@ namespace System.Threading return Pack (iocb, null); } - [CLSCompliant(false),ComVisible(false)] + [CLSCompliant(false)] unsafe public NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData) { return m_overlappedData.Pack(iocb, userData); @@ -349,7 +337,7 @@ namespace System.Threading return UnsafePack (iocb, null); } - [CLSCompliant(false), ComVisible(false)] + [CLSCompliant(false)] unsafe public NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) { return m_overlappedData.UnsafePack(iocb, userData); diff --git a/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs b/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs index 45d24fef49..32b63153c4 100644 --- a/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs +++ b/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs @@ -15,7 +15,6 @@ namespace System.Threading { - using System.Security.Permissions; using System.Threading; using System.Runtime.InteropServices; diff --git a/src/mscorlib/src/System/Threading/ReaderWriterLock.cs b/src/mscorlib/src/System/Threading/ReaderWriterLock.cs deleted file mode 100644 index e35ac7685b..0000000000 --- a/src/mscorlib/src/System/Threading/ReaderWriterLock.cs +++ /dev/null @@ -1,276 +0,0 @@ -// 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; - - [ComVisible(true)] - public sealed class ReaderWriterLock: CriticalFinalizerObject - { - /* - * Constructor - */ - public ReaderWriterLock() - { - PrivateInitialize(); - } - - /* - * Destructor - */ - ~ReaderWriterLock() - { - PrivateDestruct(); - } - - /* - * Property that returns TRUE if the reader lock is held - * by the current thread - */ - public bool IsReaderLockHeld { - [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 { - [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 { - get { - return(PrivateGetWriterSeqNum()); - } - } - - /* - * Acquires reader lock. The thread will block if a different - * thread has writer lock. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void AcquireReaderLockInternal(int millisecondsTimeout); - - public void AcquireReaderLock(int millisecondsTimeout) - { - AcquireReaderLockInternal(millisecondsTimeout); - } - - - public void AcquireReaderLock(TimeSpan timeout) - { - long tm = (long)timeout.TotalMilliseconds; - if (tm < -1 || tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(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 - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void AcquireWriterLockInternal(int millisecondsTimeout); - - public void AcquireWriterLock(int millisecondsTimeout) - { - AcquireWriterLockInternal(millisecondsTimeout); - } - - public void AcquireWriterLock(TimeSpan timeout) - { - long tm = (long)timeout.TotalMilliseconds; - if (tm < -1 || tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); - AcquireWriterLockInternal((int)tm); - } - - - /* - * Releases reader lock. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - private extern void ReleaseReaderLockInternal(); - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - public void ReleaseReaderLock() - { - ReleaseReaderLockInternal(); - } - - /* - * Releases writer lock. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - private extern void ReleaseWriterLockInternal(); - - [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. - */ - public LockCookie UpgradeToWriterLock(int millisecondsTimeout) - { - LockCookie result = new LockCookie (); - FCallUpgradeToWriterLock (ref result, millisecondsTimeout); - return result; - } - - [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(nameof(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. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void DowngradeFromWriterLockInternal(ref LockCookie lockCookie); - - public void DowngradeFromWriterLock(ref LockCookie lockCookie) - { - DowngradeFromWriterLockInternal(ref lockCookie); - } - - /* - * Releases the lock irrespective of the number of times the thread - * acquired the lock - */ - public LockCookie ReleaseLock() - { - LockCookie result = new LockCookie (); - FCallReleaseLock (ref result); - return result; - } - - [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. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void RestoreLockInternal(ref LockCookie lockCookie); - - public void RestoreLock(ref LockCookie lockCookie) - { - RestoreLockInternal(ref lockCookie); - } - - /* - * Internal helper that returns TRUE if the reader lock is held - * by the current thread - */ - [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 - */ - [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 - */ - [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 - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public extern bool AnyWritersSince(int seqNum); - - // Initialize state kept inside the lock - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void PrivateInitialize(); - - // Destruct resource associated with the lock - [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/SemaphoreSlim.cs b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs index 92d760d77d..c3b43d9585 100644 --- a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs +++ b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs @@ -16,7 +16,6 @@ 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; @@ -39,7 +38,6 @@ namespace System.Threading /// completed. /// </para> /// </remarks> - [ComVisible(false)] [DebuggerDisplay("Current Count = {m_currentCount}")] public class SemaphoreSlim : IDisposable { @@ -315,7 +313,7 @@ namespace System.Threading if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( - "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(millisecondsTimeout), millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } cancellationToken.ThrowIfCancellationRequested(); @@ -609,7 +607,7 @@ namespace System.Threading if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( - "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(millisecondsTimeout), millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } // Bail early for cancellation diff --git a/src/mscorlib/src/System/Threading/SpinLock.cs b/src/mscorlib/src/System/Threading/SpinLock.cs index 1d90890d6e..eee73ce2bf 100644 --- a/src/mscorlib/src/System/Threading/SpinLock.cs +++ b/src/mscorlib/src/System/Threading/SpinLock.cs @@ -9,19 +9,14 @@ // 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.Security.Permissions; + +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics; -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. @@ -54,7 +49,6 @@ namespace System.Threading /// concurrently. /// </para> /// </remarks> - [ComVisible(false)] [DebuggerTypeProxy(typeof(SystemThreading_SpinLockDebugView))] [DebuggerDisplay("IsHeld = {IsHeld}")] public struct SpinLock @@ -106,6 +100,14 @@ namespace System.Threading // The waiters count is calculated by m_owner & WAITERS_MASK 01111....110 private static int MAXIMUM_WAITERS = WAITERS_MASK; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int CompareExchange(ref int location, int value, int comparand, ref bool success) + { + int result = Interlocked.CompareExchange(ref location, value, comparand); + success = (result == comparand); + return result; + } + /// <summary> /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/> /// structure with the option to track thread IDs to improve debugging. @@ -158,9 +160,8 @@ namespace System.Threading 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 + 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> @@ -183,7 +184,22 @@ namespace System.Threading /// </exception> public void TryEnter(ref bool lockTaken) { - TryEnter(0, ref lockTaken); + int observedOwner = m_owner; + if (((observedOwner & LOCK_ID_DISABLE_MASK) == 0) | lockTaken) + { + // Thread tracking enabled or invalid arg. Take slow path. + ContinueTryEnter(0, ref lockTaken); + } + else if ((observedOwner & LOCK_ANONYMOUS_OWNED) != 0) + { + // Lock already held by someone + lockTaken = false; + } + else + { + // Lock wasn't held; try to acquire it. + CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken); + } } /// <summary> @@ -219,7 +235,7 @@ namespace System.Threading if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); + nameof(timeout), timeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } // Call reliable enter with the int-based timeout milliseconds @@ -254,7 +270,7 @@ namespace System.Threading 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 + 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 } @@ -271,13 +287,13 @@ namespace System.Threading if (lockTaken) { lockTaken = false; - throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); + throw new System.ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); + nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } uint startTime = 0; @@ -311,7 +327,7 @@ namespace System.Threading observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { - if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) + if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Aquired lock return; @@ -331,7 +347,7 @@ namespace System.Threading 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 ; + turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1; } //***Step 2. Spinning @@ -353,7 +369,7 @@ namespace System.Threading : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); - if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) + if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } @@ -381,7 +397,7 @@ namespace System.Threading : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); - if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) + if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } @@ -431,7 +447,6 @@ namespace System.Threading } spinner.SpinOnce(); } - } /// <summary> @@ -444,11 +459,11 @@ namespace System.Threading 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; + int m_newOwner = Environment.CurrentManagedThreadId; if (m_owner == m_newOwner) { // We don't allow lock recursion. - throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException")); + throw new LockRecursionException(SR.SpinLock_TryEnter_LockRecursionException); } @@ -457,7 +472,6 @@ namespace System.Threading // 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(); @@ -466,7 +480,7 @@ namespace System.Threading if (m_owner == lockUnowned) { - if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) + if (CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) { return; } @@ -491,7 +505,6 @@ namespace System.Threading /// <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 @@ -517,15 +530,14 @@ namespace System.Threading /// <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) + // method inlining, so this is optimized for useMemoryBarrier=false and Exit() overload optimized for useMemoryBarrier=true. + int tmpOwner = m_owner; + if ((tmpOwner & LOCK_ID_DISABLE_MASK) != 0 & !useMemoryBarrier) { - int tmpOwner = m_owner; m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); } else @@ -545,7 +557,7 @@ namespace System.Threading if (threadTrackingEnabled && !IsHeldByCurrentThread) { throw new System.Threading.SynchronizationLockException( - Environment.GetResourceString("SpinLock_Exit_SynchronizationLockException")); + SR.SpinLock_Exit_SynchronizationLockException); } if (useMemoryBarrier) @@ -554,7 +566,6 @@ namespace System.Threading Interlocked.Exchange(ref m_owner, LOCK_UNOWNED); else Interlocked.Decrement(ref m_owner); - } else { @@ -565,9 +576,7 @@ namespace System.Threading int tmpOwner = m_owner; m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); } - } - } /// <summary> @@ -575,7 +584,6 @@ namespace System.Threading /// </summary> public bool IsHeld { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] get { if (IsThreadOwnerTrackingEnabled) @@ -601,21 +609,19 @@ namespace System.Threading /// </exception> public bool IsHeldByCurrentThread { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] get { if (!IsThreadOwnerTrackingEnabled) { - throw new InvalidOperationException(Environment.GetResourceString("SpinLock_IsHeldByCurrentThread")); + throw new InvalidOperationException(SR.SpinLock_IsHeldByCurrentThread); } - return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Thread.CurrentThread.ManagedThreadId); + return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Environment.CurrentManagedThreadId); } } /// <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; } } diff --git a/src/mscorlib/src/System/Threading/SpinWait.cs b/src/mscorlib/src/System/Threading/SpinWait.cs index 1b31407e0f..8431f6564f 100644 --- a/src/mscorlib/src/System/Threading/SpinWait.cs +++ b/src/mscorlib/src/System/Threading/SpinWait.cs @@ -12,7 +12,6 @@ using System; using System.Runtime.ConstrainedExecution; -using System.Security.Permissions; using System.Threading; using System.Diagnostics; using System.Diagnostics.Contracts; diff --git a/src/mscorlib/src/System/Threading/SynchronizationContext.cs b/src/mscorlib/src/System/Threading/SynchronizationContext.cs index 5531597229..f4b3c79409 100644 --- a/src/mscorlib/src/System/Threading/SynchronizationContext.cs +++ b/src/mscorlib/src/System/Threading/SynchronizationContext.cs @@ -13,12 +13,9 @@ 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; @@ -29,14 +26,12 @@ namespace System.Threading using System.Diagnostics.CodeAnalysis; -#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT [Flags] enum SynchronizationContextProperties { None = 0, RequireWaitNotification = 0x1 }; -#endif #if FEATURE_COMINTEROP && FEATURE_APPX // @@ -52,15 +47,12 @@ namespace System.Threading 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); @@ -109,7 +101,6 @@ namespace System.Threading { return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0); } -#endif public virtual void Send(SendOrPostCallback d, Object state) @@ -137,10 +128,8 @@ namespace System.Threading { } -#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT // Method called when the CLR does a wait operation [CLSCompliant(false)] - [PrePrepareMethod] public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { return WaitHelper(waitHandles, waitAll, millisecondsTimeout); @@ -148,8 +137,6 @@ namespace System.Threading // Method that can be called by Wait overrides [CLSCompliant(false)] - [PrePrepareMethod] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { if (waitHandles == null) @@ -164,22 +151,14 @@ namespace System.Threading // Static helper to which the above method can delegate to in order to get the default // COM behavior. [CLSCompliant(false)] - [PrePrepareMethod] [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private static extern int WaitHelperNative(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); -#endif public static void SetSynchronizationContext(SynchronizationContext syncContext) { Thread.CurrentThread.SynchronizationContext = syncContext; } - public static void SetThreadStaticContext(SynchronizationContext syncContext) - { - Thread.CurrentThread.SynchronizationContext = syncContext; - } - public static SynchronizationContext Current { get @@ -260,11 +239,9 @@ namespace System.Threading return new SynchronizationContext(); } -#if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT 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 index 0610a6063d..de42c1f232 100644 --- a/src/mscorlib/src/System/Threading/SynchronizationLockException.cs +++ b/src/mscorlib/src/System/Threading/SynchronizationLockException.cs @@ -17,7 +17,6 @@ namespace System.Threading { using System; using System.Runtime.Serialization; - [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public class SynchronizationLockException : SystemException { public SynchronizationLockException() diff --git a/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs b/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs deleted file mode 100644 index 71eb787c5e..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/BeginEndAwaitableAdapter.cs +++ /dev/null @@ -1,157 +0,0 @@ -// 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; -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) => { - - Debug.Assert(asyncResult != null); - Debug.Assert(asyncResult.IsCompleted); - Debug.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) { - - Debug.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> - public void UnsafeOnCompleted(Action continuation) { - - Debug.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) { - - Debug.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() { - - Debug.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 index c7a96b0394..a87406a493 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs @@ -20,7 +20,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Security; -using System.Security.Permissions; namespace System.Threading.Tasks { diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs index c98e219e86..137afa11f5 100644 --- a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs @@ -12,7 +12,6 @@ using System; using System.Security; -using System.Security.Permissions; using System.Runtime.CompilerServices; using System.Threading; using System.Diagnostics; @@ -277,13 +276,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -306,13 +303,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -337,13 +332,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -379,13 +372,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, scheduler); } /// <summary> @@ -406,13 +397,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -437,13 +426,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -470,13 +457,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -514,12 +499,10 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, scheduler); } // @@ -604,11 +587,9 @@ namespace System.Threading.Tasks /// <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); + return FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler); } /// <summary> @@ -630,14 +611,12 @@ namespace System.Threading.Tasks /// 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); + return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler); } @@ -665,27 +644,23 @@ namespace System.Threading.Tasks /// 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); + return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler); } - // internal overload that supports StackCrawlMark - // We also need this logic broken out into a static method so that the similar TaskFactory.FromAsync() + // 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( IAsyncResult asyncResult, Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> endAction, TaskCreationOptions creationOptions, - TaskScheduler scheduler, - ref StackCrawlMark stackMark) + TaskScheduler scheduler) { if (asyncResult == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult); @@ -714,12 +689,12 @@ namespace System.Threading.Tasks // 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 + Task t = new Task(new Action<object>(delegate { FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true); - }, + }), (object)null, null, - default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null, ref stackMark); + default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Verbose, t.Id, "TaskFactory.FromAsync Callback", 0); @@ -1428,14 +1403,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1459,14 +1432,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -1496,14 +1467,12 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1543,15 +1512,13 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } /// <summary> @@ -1571,14 +1538,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1603,15 +1568,13 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -1642,15 +1605,13 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1691,15 +1652,13 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } @@ -1707,7 +1666,7 @@ namespace System.Threading.Tasks // 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) + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); @@ -1737,7 +1696,7 @@ namespace System.Threading.Tasks return starter.ContinueWith<TResult>( // use a cached delegate GenericDelegateCache<TAntecedentResult, TResult>.CWAllFuncDelegate, - continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationFunction, scheduler, cancellationToken, continuationOptions); } else { @@ -1746,7 +1705,7 @@ namespace System.Threading.Tasks return starter.ContinueWith<TResult>( // use a cached delegate GenericDelegateCache<TAntecedentResult, TResult>.CWAllActionDelegate, - continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationAction, scheduler, cancellationToken, continuationOptions); } } @@ -1754,7 +1713,7 @@ namespace System.Threading.Tasks // 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) + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); @@ -1790,7 +1749,7 @@ namespace System.Threading.Tasks completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); return ((Func<Task[], TResult>)state)(completedTasks.Result); }, - continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationFunction, scheduler, cancellationToken, continuationOptions); } else { @@ -1804,7 +1763,7 @@ namespace System.Threading.Tasks completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); }, - continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationAction, scheduler, cancellationToken, continuationOptions); } } @@ -1828,14 +1787,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1859,14 +1816,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -1896,14 +1851,12 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1943,15 +1896,13 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } /// <summary> @@ -1971,14 +1922,12 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2003,15 +1952,13 @@ namespace System.Threading.Tasks /// <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); + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2042,15 +1989,13 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2091,22 +2036,20 @@ namespace System.Threading.Tasks /// 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); + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } // 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) + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); @@ -2136,7 +2079,7 @@ namespace System.Threading.Tasks //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); + continuationFunction, scheduler, cancellationToken, continuationOptions); } else { @@ -2145,7 +2088,7 @@ namespace System.Threading.Tasks //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); + continuationAction, scheduler, cancellationToken, continuationOptions); } } @@ -2154,7 +2097,7 @@ namespace System.Threading.Tasks // 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) + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); @@ -2182,7 +2125,7 @@ namespace System.Threading.Tasks return starter.ContinueWith<TResult>( // Use a cached delegate GenericDelegateCache<TAntecedentResult, TResult>.CWAnyFuncDelegate, - continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationFunction, scheduler, cancellationToken, continuationOptions); } else { @@ -2190,7 +2133,7 @@ namespace System.Threading.Tasks return starter.ContinueWith<TResult>( // Use a cached delegate GenericDelegateCache<TAntecedentResult,TResult>.CWAnyActionDelegate, - continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + continuationAction, scheduler, cancellationToken, continuationOptions); } } } diff --git a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs index b8155d017e..32efd771a0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs +++ b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs @@ -19,16 +19,16 @@ namespace Windows.Foundation.Diagnostics [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); + { + 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")] diff --git a/src/mscorlib/src/System/Threading/Tasks/Parallel.cs b/src/mscorlib/src/System/Threading/Tasks/Parallel.cs deleted file mode 100644 index 7808943870..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/Parallel.cs +++ /dev/null @@ -1,3593 +0,0 @@ -// 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(nameof(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> - 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(nameof(actions)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(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(nameof(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(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(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(nameof(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(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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) - { - Debug.Assert(((body == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) + (bodyWithLocal == null ? 0 : 1)) == 1, - "expected exactly one body function to be supplied"); - Debug.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) - { - Debug.Assert(sharedPStateFlags != null); - state = new ParallelLoopState32(sharedPStateFlags); - } - else if (bodyWithLocal != null) - { - Debug.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) - { - Debug.Assert(((body == null ? 0 : 1) + (bodyWithState == null ? 0 : 1) + (bodyWithLocal == null ? 0 : 1)) == 1, - "expected exactly one body function to be supplied"); - Debug.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) - { - Debug.Assert(sharedPStateFlags != null); - state = new ParallelLoopState64(sharedPStateFlags); - } - else if (bodyWithLocal != null) - { - Debug.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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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) - { - Debug.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"); - Debug.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) - { - Debug.Assert(array != null); - Debug.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<T>'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) - { - Debug.Assert(list != null); - Debug.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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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(nameof(source)); - } - if (body == null) - { - throw new ArgumentNullException(nameof(body)); - } - if (localInit == null) - { - throw new ArgumentNullException(nameof(localInit)); - } - if (localFinally == null) - { - throw new ArgumentNullException(nameof(localFinally)); - } - if (parallelOptions == null) - { - throw new ArgumentNullException(nameof(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) - { - Debug.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"); - Debug.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>; - Debug.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 - Debug.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() - { - Debug.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 deleted file mode 100644 index 6a62cf8977..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/ParallelLoopState.cs +++ /dev/null @@ -1,641 +0,0 @@ -// 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> - [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 - { - Debug.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 - { - Debug.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() - { - Debug.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 deleted file mode 100644 index 49f61a6614..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/ParallelRangeManager.cs +++ /dev/null @@ -1,279 +0,0 @@ -// 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; -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); - - Debug.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 - // - Debug.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 - Debug.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() - { - Debug.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 index 6b9dfbbe37..545bf9a5e5 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs @@ -52,11 +52,6 @@ namespace System.Threading.Tasks /// <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> @@ -80,10 +75,6 @@ namespace System.Threading.Tasks /// <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> @@ -260,143 +251,6 @@ namespace System.Threading.Tasks 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 @@ -452,17 +306,6 @@ namespace System.Threading.Tasks } } - /// <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) - { - Debug.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 @@ -520,23 +363,9 @@ namespace System.Threading.Tasks 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 { diff --git a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs index 325aa91b44..12cc1daa63 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs @@ -14,7 +14,6 @@ using System; using System.Collections.Generic; using System.Text; using System.Security; -using System.Security.Permissions; using System.Runtime.CompilerServices; namespace System.Threading.Tasks @@ -61,17 +60,6 @@ namespace System.Threading.Tasks /// <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 { @@ -203,195 +191,6 @@ namespace System.Threading.Tasks //----------------------------------------------------------------------------------- // - // 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> - [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> - [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> - [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 // @@ -512,7 +311,7 @@ namespace System.Threading.Tasks 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) + int TaskID, TaskWaitBehavior Behavior, int ContinueWithTaskID) { if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) { @@ -534,8 +333,10 @@ namespace System.Threading.Tasks Guid childActivityId = CreateGuidForTaskID(TaskID); WriteEventWithRelatedActivityIdCore(TASKWAITBEGIN_ID, &childActivityId, 5, eventPayload); } - else + else + { WriteEventCore(TASKWAITBEGIN_ID, 5, eventPayload); + } } } } @@ -688,7 +489,7 @@ namespace System.Threading.Tasks } } - [NonEvent, System.Security.SecuritySafeCritical] + [NonEvent] 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) @@ -697,7 +498,7 @@ namespace System.Threading.Tasks WriteEvent(20, TaskID, Object); } - [NonEvent, System.Security.SecuritySafeCritical] + [NonEvent] unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); } [Event(21, Keywords = Keywords.Debug)] @@ -707,9 +508,6 @@ namespace System.Threading.Tasks 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); } diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs index cf081f75fd..7013c5c5e0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/Task.cs +++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs @@ -18,7 +18,6 @@ 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; @@ -147,7 +146,6 @@ namespace System.Threading.Tasks 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 @@ -365,12 +363,9 @@ namespace System.Threading.Tasks /// </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> @@ -383,12 +378,9 @@ namespace System.Threading.Tasks /// <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> @@ -406,12 +398,9 @@ namespace System.Threading.Tasks /// 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> @@ -433,12 +422,9 @@ namespace System.Threading.Tasks /// <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); } @@ -450,12 +436,9 @@ namespace System.Threading.Tasks /// <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> @@ -470,12 +453,9 @@ namespace System.Threading.Tasks /// <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> @@ -494,12 +474,9 @@ namespace System.Threading.Tasks /// 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> @@ -522,19 +499,9 @@ namespace System.Threading.Tasks /// <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> @@ -557,10 +524,8 @@ namespace System.Threading.Tasks } 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)) + // Keep a link to the parent if attached + if (parent != null && (creationOptions & TaskCreationOptions.AttachedToParent) != 0) { EnsureContingentPropertiesInitializedUnsafe().m_parent = parent; } @@ -586,6 +551,10 @@ namespace System.Threading.Tasks m_stateObject = state; m_taskScheduler = scheduler; + Debug.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null, + "Captured an ExecutionContext when one was already captured."); + CapturedContext = ExecutionContext.Capture(); + // Check for validity of options if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | @@ -602,22 +571,13 @@ namespace System.Threading.Tasks // Check the validity of internalOptions int illegalInternalOptions = (int) (internalOptions & - ~(InternalTaskOptions.SelfReplicating | - InternalTaskOptions.ChildReplica | - InternalTaskOptions.PromiseTask | + ~(InternalTaskOptions.PromiseTask | InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation | InternalTaskOptions.QueuedByRuntime)); Debug.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. Debug.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags"); Debug.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits"); @@ -648,9 +608,7 @@ namespace System.Threading.Tasks // 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) { - Debug.Assert((internalOptions & - (InternalTaskOptions.ChildReplica | InternalTaskOptions.SelfReplicating | InternalTaskOptions.ContinuationTask)) == 0, - "TaskConstructorCore: Did not expect to see cancelable token for replica/replicating or continuation task."); + Debug.Assert((internalOptions & InternalTaskOptions.ContinuationTask) == 0, "TaskConstructorCore: Did not expect to see cancelable token for continuation task."); AssignCancellationToken(cancellationToken, null, null); } @@ -757,24 +715,6 @@ namespace System.Threading.Tasks } } - - /// <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> - - internal void PossiblyCaptureContext(ref StackCrawlMark stackMark) - { - Debug.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 { @@ -989,16 +929,14 @@ namespace System.Threading.Tasks /// </summary> internal void AddNewChild() { - Debug.Assert(Task.InternalCurrent == this || this.IsSelfReplicatingRoot, "Task.AddNewChild(): Called from an external context"); + Debug.Assert(Task.InternalCurrent == this, "Task.AddNewChild(): Called from an external context"); var props = EnsureContingentPropertiesInitialized(); - if (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot) + if (props.m_completionCountdown == 1) { // 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 @@ -1264,7 +1202,7 @@ namespace System.Threading.Tasks // 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) + TaskCreationOptions options, InternalTaskOptions internalOptions) { // Validate arguments. if (scheduler == null) @@ -1276,7 +1214,6 @@ namespace System.Threading.Tasks // 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; @@ -1615,23 +1552,10 @@ namespace System.Threading.Tasks /// 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; + public static TaskFactory Factory { get; } = new TaskFactory(); /// <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; - } - } + public static Task CompletedTask { get; } = new Task(false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); /// <summary> /// Provides an event that can be used to wait for completion. @@ -1663,35 +1587,6 @@ namespace System.Threading.Tasks } } - /// <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. @@ -1737,7 +1632,7 @@ namespace System.Threading.Tasks } else { - return m_contingentProperties?.m_capturedContext ?? ExecutionContext.PreAllocatedDefault; + return m_contingentProperties?.m_capturedContext ?? ExecutionContext.Default; } } set @@ -1747,7 +1642,7 @@ namespace System.Threading.Tasks { m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL; } - else if (!value.IsPreAllocatedDefault) // not the default context, then inflate the contingent properties and set it + else if (value != ExecutionContext.Default) // not the default context, then inflate the contingent properties and set it { EnsureContingentPropertiesInitializedUnsafe().m_capturedContext = value; } @@ -1755,32 +1650,6 @@ namespace System.Threading.Tasks } } - /// <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 @@ -1920,7 +1789,7 @@ namespace System.Threading.Tasks catch (ThreadAbortException tae) { AddException(tae); - FinishThreadAbortedTask(true, false); + FinishThreadAbortedTask(delegateRan:false); } catch (Exception e) { @@ -1930,10 +1799,10 @@ namespace System.Threading.Tasks 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. + // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew(), + // because 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) { @@ -2192,11 +2061,9 @@ namespace System.Threading.Tasks 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) || + (props.m_completionCountdown == 1) || // 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. @@ -2424,19 +2291,13 @@ namespace System.Threading.Tasks /// 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) + internal void FinishThreadAbortedTask(bool delegateRan) { - Debug.Assert(!bTAEAddedToExceptionHolder || m_contingentProperties?.m_exceptionsHolder != null, - "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized"); + Debug.Assert(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); + 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(). @@ -2447,220 +2308,33 @@ namespace System.Threading.Tasks } 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. + /// Executes the task. This method will only be called once, and handles any necessary exception marshaling. /// </summary> private void Execute() { - if (IsSelfReplicatingRoot) + try { - ExecuteSelfReplicating(this); + InnerInvoke(); } - else + catch (ThreadAbortException tae) { - 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); + // 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); - } + // 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(delegateRan: true); } - } - - // 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 + catch (Exception exn) { - 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); + // Record this exception in the task's exception list + HandleException(exn); + } } /// <summary> @@ -2683,7 +2357,7 @@ namespace System.Threading.Tasks if (!IsCompleted) { HandleException(tae); - FinishThreadAbortedTask(true, false); + FinishThreadAbortedTask(delegateRan:false); } } @@ -2693,10 +2367,10 @@ namespace System.Threading.Tasks /// /// </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> + /// in codepaths servicing user provided TaskSchedulers. The ThreadPool scheduler doesn't need this. </param> internal bool ExecuteEntry(bool bPreventDoubleExecution) { - if (bPreventDoubleExecution || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0)) + if (bPreventDoubleExecution) { int previousState = 0; @@ -2772,18 +2446,10 @@ namespace System.Threading.Tasks } 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); + ExecutionContext.Run(ec, s_ecCallback, this); } if (AsyncCausalityTracer.LoggingOn) @@ -2810,16 +2476,7 @@ namespace System.Threading.Tasks } } - // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical - private static ContextCallback s_ecCallback; - - private static void ExecutionContextCallback(object obj) - { - Task task = obj as Task; - Debug.Assert(task != null, "expected a task object"); - task.Execute(); - } - + private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).Execute(); /// <summary> /// The actual code which invokes the body of the task. This can be overriden in derived types. @@ -2844,21 +2501,6 @@ namespace System.Threading.Tasks } /// <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> @@ -2917,10 +2559,9 @@ namespace System.Threading.Tasks /// 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> internal void SetContinuationForAwait( - Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark) + Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext) { Contract.Requires(continuationAction != null); @@ -2941,7 +2582,7 @@ namespace System.Threading.Tasks var syncCtx = SynchronizationContext.CurrentNoFlow; if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) { - tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark); + tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext); } else { @@ -2950,7 +2591,7 @@ namespace System.Threading.Tasks var scheduler = TaskScheduler.InternalCurrent; if (scheduler != null && scheduler != TaskScheduler.Default) { - tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark); + tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext); } } } @@ -2962,7 +2603,7 @@ namespace System.Threading.Tasks // 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); + tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true); } // Now register the continuation, and if we couldn't register it because the task is already completing, @@ -3190,7 +2831,7 @@ namespace System.Threading.Tasks 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()); + this.Id, TplEtwProvider.TaskWaitBehavior.Synchronous, 0); } bool returnValue = IsCompleted; @@ -3377,7 +3018,7 @@ namespace System.Threading.Tasks } } - bool bRequiresAtomicStartTransition = (ts != null && ts.RequiresAtomicStartTransition) || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0); + bool bRequiresAtomicStartTransition = ts != null && ts.RequiresAtomicStartTransition; if (!bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition) { @@ -3715,11 +3356,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -3742,11 +3381,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -3771,11 +3408,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -3806,11 +3441,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -3851,17 +3484,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions); } // 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) + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null action if (continuationAction == null) @@ -3882,8 +3513,7 @@ namespace System.Threading.Tasks Task continuationTask = new ContinuationTaskFromTask( this, continuationAction, null, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -3913,11 +3543,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -3941,11 +3569,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -3971,11 +3597,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -4007,11 +3631,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -4053,17 +3675,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions); } // 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) + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null action if (continuationAction == null) @@ -4084,8 +3704,7 @@ namespace System.Threading.Tasks Task continuationTask = new ContinuationTaskFromTask( this, continuationAction, state, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -4118,12 +3737,10 @@ namespace System.Threading.Tasks /// <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); + TaskContinuationOptions.None); } @@ -4150,11 +3767,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -4182,11 +3797,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -4220,11 +3833,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -4268,17 +3879,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, scheduler, cancellationToken, continuationOptions); } // 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) + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null function if (continuationFunction == null) @@ -4299,8 +3908,7 @@ namespace System.Threading.Tasks Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>( this, continuationFunction, null, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -4333,12 +3941,10 @@ namespace System.Threading.Tasks /// <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); + TaskContinuationOptions.None); } @@ -4366,11 +3972,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -4399,11 +4003,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -4438,11 +4040,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -4487,17 +4087,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions); } // 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) + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null function if (continuationFunction == null) @@ -4518,8 +4116,7 @@ namespace System.Threading.Tasks Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>( this, continuationFunction, state, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -5207,49 +4804,6 @@ namespace System.Threading.Tasks } /// <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). @@ -5582,12 +5136,10 @@ namespace System.Threading.Tasks /// <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); + TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None); } /// <summary> @@ -5602,12 +5154,10 @@ namespace System.Threading.Tasks /// <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); + TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None); } /// <summary> @@ -5618,12 +5168,10 @@ namespace System.Threading.Tasks /// <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); + TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default); } /// <summary> @@ -5638,12 +5186,10 @@ namespace System.Threading.Tasks /// <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); + TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default); } /// <summary> @@ -6693,102 +6239,6 @@ namespace System.Threading.Tasks 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> @@ -6856,10 +6306,8 @@ namespace System.Threading.Tasks /// <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 diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs index 320f704f09..bf9f9cbb2c 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs @@ -17,7 +17,6 @@ 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. @@ -203,22 +202,6 @@ namespace System.Threading.Tasks 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) - { - Debug.Assert(exceptions != null); -#if DEBUG - foreach(var edi in exceptions) Debug.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 diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs index 70b9418dbf..3c6ccd8dd4 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs @@ -29,13 +29,12 @@ namespace System.Threading.Tasks private Task m_antecedent; public ContinuationTaskFromTask( - Task antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) : + Task antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : 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> @@ -77,13 +76,12 @@ namespace System.Threading.Tasks private Task m_antecedent; public ContinuationResultTaskFromTask( - Task antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) : + Task antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : 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> @@ -125,13 +123,12 @@ namespace System.Threading.Tasks private Task<TAntecedentResult> m_antecedent; public ContinuationTaskFromResultTask( - Task<TAntecedentResult> antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) : + Task<TAntecedentResult> antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : 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> @@ -173,13 +170,12 @@ namespace System.Threading.Tasks private Task<TAntecedentResult> m_antecedent; public ContinuationResultTaskFromResultTask( - Task<TAntecedentResult> antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) : + Task<TAntecedentResult> antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : 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> @@ -392,10 +388,9 @@ namespace System.Threading.Tasks /// <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> internal SynchronizationContextAwaitTaskContinuation( - SynchronizationContext context, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) : - base(action, flowExecutionContext, ref stackMark) + SynchronizationContext context, Action action, bool flowExecutionContext) : + base(action, flowExecutionContext) { Debug.Assert(context != null); m_syncContext = context; @@ -479,10 +474,9 @@ namespace System.Threading.Tasks /// <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> internal TaskSchedulerAwaitTaskContinuation( - TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) : - base(action, flowExecutionContext, ref stackMark) + TaskScheduler scheduler, Action action, bool flowExecutionContext) : + base(action, flowExecutionContext) { Debug.Assert(scheduler != null); m_scheduler = scheduler; @@ -543,29 +537,13 @@ namespace System.Threading.Tasks /// <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> - 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> internal AwaitTaskContinuation(Action action, bool flowExecutionContext) { Contract.Requires(action != null); m_action = action; if (flowExecutionContext) { - m_capturedContext = ExecutionContext.FastCapture(); + m_capturedContext = ExecutionContext.Capture(); } } @@ -670,11 +648,7 @@ namespace System.Threading.Tasks // 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(); } + ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action); } } finally @@ -689,8 +663,7 @@ namespace System.Threading.Tasks void IThreadPoolWorkItem.ExecuteWorkItem() { // inline the fast path - if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled() - ) + if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled()) { m_action(); } @@ -739,7 +712,7 @@ namespace System.Threading.Tasks // 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); + else ExecutionContext.Run(m_capturedContext, callback, state); } catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs { @@ -749,9 +722,6 @@ namespace System.Threading.Tasks { // 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(); } } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs index 45817dab23..ee1112a93f 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs @@ -149,23 +149,6 @@ namespace System.Threading.Tasks /// 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> diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs index aa4c2df74b..89ba2988ca 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; using System.Security; -using System.Security.Permissions; using System.Runtime.CompilerServices; using System.Threading; using System.Diagnostics; @@ -225,7 +224,7 @@ namespace System.Threading.Tasks TaskCreationOptions.PreferFairness | TaskCreationOptions.RunContinuationsAsynchronously)) != 0) { - throw new ArgumentOutOfRangeException(nameof(creationOptions)); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions); } Contract.EndContractBlock(); } @@ -293,13 +292,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None); } /// <summary> @@ -320,13 +317,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None); } /// <summary> @@ -350,13 +345,11 @@ namespace System.Threading.Tasks /// 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); + InternalTaskOptions.None); } /// <summary> @@ -391,22 +384,11 @@ namespace System.Threading.Tasks /// 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); + InternalTaskOptions.None); } @@ -427,13 +409,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None); } @@ -458,13 +438,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None); } /// <summary> @@ -490,13 +468,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None); } /// <summary> @@ -533,14 +509,12 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None); } /// <summary> @@ -562,13 +536,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } @@ -595,13 +567,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -629,13 +599,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -674,13 +642,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, scheduler); } /// <summary> @@ -704,13 +670,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } @@ -739,13 +703,11 @@ namespace System.Threading.Tasks /// 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); + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -775,13 +737,11 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// <summary> @@ -822,14 +782,12 @@ namespace System.Threading.Tasks /// 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); + creationOptions, InternalTaskOptions.None, scheduler); } // @@ -850,13 +808,11 @@ namespace System.Threading.Tasks /// <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); + return FromAsync(asyncResult, endMethod, m_defaultCreationOptions, DefaultScheduler); } /// <summary> @@ -878,14 +834,12 @@ namespace System.Threading.Tasks /// 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); + return FromAsync(asyncResult, endMethod, creationOptions, DefaultScheduler); } /// <summary> @@ -911,26 +865,13 @@ namespace System.Threading.Tasks /// 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); + return TaskFactory<VoidTaskResult>.FromAsyncImpl(asyncResult, null, endMethod, creationOptions, scheduler); } /// <summary> @@ -1228,12 +1169,10 @@ namespace System.Threading.Tasks /// <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); + return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler); } /// <summary> @@ -1258,12 +1197,10 @@ namespace System.Threading.Tasks /// 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); + return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler); } /// <summary> @@ -1292,12 +1229,10 @@ namespace System.Threading.Tasks /// 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); + return TaskFactory<TResult>.FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler); } /// <summary> @@ -1606,7 +1541,7 @@ namespace System.Threading.Tasks TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning)) != 0) { - throw new ArgumentOutOfRangeException(nameof(creationOptions)); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions); } } @@ -1798,14 +1733,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } @@ -1830,14 +1763,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -1867,14 +1798,12 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -1914,15 +1843,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler); } /// <summary> @@ -1942,14 +1869,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } @@ -1975,15 +1900,13 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2014,15 +1937,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2063,15 +1984,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler); } /// <summary> @@ -2094,14 +2013,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } @@ -2130,14 +2047,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2171,14 +2086,12 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2222,15 +2135,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } @@ -2255,14 +2166,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2291,15 +2200,13 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2334,15 +2241,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2387,15 +2292,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } // @@ -2505,7 +2408,23 @@ namespace System.Threading.Tasks checkArgsOnly = true; } // Otherwise, add the completion action and keep going. - else task.AddCompletionAction(promise); + else + { + task.AddCompletionAction(promise); + if (promise.IsCompleted) + { + // One of the previous tasks that already had its continuation registered may have + // raced to complete with our adding the continuation to this task. The completion + // routine would have gone through and removed the continuation from all of the tasks + // with which it was already registered, but if the race causes this continuation to + // be added after that, it'll never be removed. As such, after adding the continuation, + // we check to see whether the promise has already completed, and if it has, we try to + // manually remove the continuation from this task. If it was already removed, it'll be + // a nop, and if we race to remove it, the synchronization in RemoveContinuation will + // keep things consistent. + task.RemoveContinuation(promise); + } + } } return promise; @@ -2528,14 +2447,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2559,14 +2476,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2596,14 +2511,12 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2643,15 +2556,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler); } @@ -2675,14 +2586,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2710,14 +2619,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2751,14 +2658,12 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null,continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null,continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2802,15 +2707,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } /// <summary> @@ -2834,12 +2737,10 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2868,15 +2769,13 @@ namespace System.Threading.Tasks /// <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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -2911,15 +2810,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2964,15 +2861,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationFunction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } @@ -2993,14 +2888,12 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -3025,15 +2918,13 @@ namespace System.Threading.Tasks /// <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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -3064,15 +2955,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -3113,15 +3002,13 @@ namespace System.Threading.Tasks /// 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(nameof(continuationAction)); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler, ref stackMark); + return TaskFactory<VoidTaskResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, null, continuationAction, continuationOptions, cancellationToken, scheduler); } // Check task array and return a defensive copy. diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs index fad3fc06c5..d68c3fedc4 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs @@ -16,7 +16,6 @@ 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; diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs index 0c3fec89b7..15136f12bf 100644 --- a/src/mscorlib/src/System/Threading/Tasks/future.cs +++ b/src/mscorlib/src/System/Threading/Tasks/future.cs @@ -16,7 +16,6 @@ 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; @@ -128,13 +127,10 @@ namespace System.Threading.Tasks /// <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); } @@ -152,13 +148,10 @@ namespace System.Threading.Tasks /// <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> @@ -179,12 +172,9 @@ namespace System.Threading.Tasks /// 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> @@ -209,12 +199,9 @@ namespace System.Threading.Tasks /// <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> @@ -228,13 +215,10 @@ namespace System.Threading.Tasks /// <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> @@ -252,13 +236,10 @@ namespace System.Threading.Tasks /// <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> @@ -280,13 +261,10 @@ namespace System.Threading.Tasks /// 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); } @@ -313,23 +291,10 @@ namespace System.Threading.Tasks /// <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> @@ -341,24 +306,10 @@ namespace System.Threading.Tasks /// <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> @@ -371,22 +322,16 @@ namespace System.Threading.Tasks /// <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) + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) { if (function == null) { @@ -396,13 +341,9 @@ namespace System.Threading.Tasks { 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); + Task<TResult> f = new Task<TResult>(function, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler); f.ScheduleAndStart(false); return f; @@ -410,7 +351,7 @@ namespace System.Threading.Tasks // 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) + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) { if (function == null) { @@ -420,13 +361,9 @@ namespace System.Threading.Tasks { 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); + Task<TResult> f = new Task<TResult>(function, state, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler); f.ScheduleAndStart(false); return f; @@ -726,11 +663,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } @@ -754,11 +689,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -784,11 +717,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -819,11 +750,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -864,17 +793,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions); } // 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) + TaskContinuationOptions continuationOptions) { if (continuationAction == null) { @@ -895,8 +822,7 @@ namespace System.Threading.Tasks Task continuationTask = new ContinuationTaskFromResultTask<TResult>( this, continuationAction, null, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -926,11 +852,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } @@ -955,11 +879,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -986,11 +908,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -1022,11 +942,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -1068,17 +986,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions); } // 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) + TaskContinuationOptions continuationOptions) { if (continuationAction == null) { @@ -1099,8 +1015,7 @@ namespace System.Threading.Tasks Task continuationTask = new ContinuationTaskFromResultTask<TResult>( this, continuationAction, state, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -1133,11 +1048,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } @@ -1164,11 +1077,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -1196,11 +1107,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -1240,11 +1149,9 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -1295,17 +1202,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, scheduler, cancellationToken, continuationOptions); } // 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) + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) { @@ -1326,8 +1231,7 @@ namespace System.Threading.Tasks Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( this, continuationFunction, null, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may @@ -1360,11 +1264,9 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None); } @@ -1392,12 +1294,10 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } /// <summary> @@ -1426,12 +1326,10 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None); } /// <summary> @@ -1472,12 +1370,10 @@ namespace System.Threading.Tasks /// 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); + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } /// <summary> @@ -1529,17 +1425,15 @@ namespace System.Threading.Tasks /// <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); + return ContinueWith<TNewResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions); } // 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) + TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) { @@ -1560,8 +1454,7 @@ namespace System.Threading.Tasks Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( this, continuationFunction, state, - creationOptions, internalOptions, - ref stackMark + creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may diff --git a/src/mscorlib/src/System/Threading/Thread.cs b/src/mscorlib/src/System/Threading/Thread.cs index 8294c20c4d..d28002729a 100644 --- a/src/mscorlib/src/System/Threading/Thread.cs +++ b/src/mscorlib/src/System/Threading/Thread.cs @@ -19,8 +19,6 @@ namespace System.Threading { using System.Runtime; using System.Runtime.InteropServices; using System; - using System.Security.Permissions; - using System.Security.Principal; using System.Globalization; using System.Collections.Generic; using System.Runtime.Serialization; @@ -103,11 +101,7 @@ namespace System.Threading { } } - // deliberately not [serializable] - [ClassInterface(ClassInterfaceType.None)] - [ComDefaultInterface(typeof(_Thread))] - [System.Runtime.InteropServices.ComVisible(true)] - public sealed class Thread : RuntimeThread, _Thread + public sealed class Thread : RuntimeThread { /*========================================================================= ** Data accessed from managed code that needs to be defined in @@ -120,10 +114,6 @@ namespace System.Threading { 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; /*========================================================================= @@ -149,47 +139,25 @@ namespace System.Threading { 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 } // Adding an empty default ctor for annotation purposes @@ -209,7 +177,7 @@ namespace System.Threading { SetStartHelper((Delegate)start,0); //0 will setup Thread with default stackSize } - public Thread(ThreadStart start, int maxStackSize) { + internal Thread(ThreadStart start, int maxStackSize) { if (start == null) { throw new ArgumentNullException(nameof(start)); } @@ -226,7 +194,7 @@ namespace System.Threading { SetStartHelper((Delegate)start, 0); } - public Thread(ParameterizedThreadStart start, int maxStackSize) { + internal Thread(ParameterizedThreadStart start, int maxStackSize) { if (start == null) { throw new ArgumentNullException(nameof(start)); } @@ -236,7 +204,6 @@ namespace System.Threading { SetStartHelper((Delegate)start, maxStackSize); } - [ComVisible(false)] public override int GetHashCode() { return m_ManagedThreadId; @@ -244,7 +211,6 @@ namespace System.Threading { extern public new int ManagedThreadId { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImplAttribute(MethodImplOptions.InternalCall)] get; } @@ -310,14 +276,11 @@ namespace System.Threading { // 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); + ExecutionContext ec = ExecutionContext.Capture(); t.SetExecutionContextHelper(ec); } - IPrincipal principal = null; - StartInternal(principal, ref stackMark); + StartInternal(ref stackMark); } internal ExecutionContext ExecutionContext @@ -333,30 +296,7 @@ namespace System.Threading { } [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void StartInternal(IPrincipal principal, ref StackCrawlMark stackMark); -#if FEATURE_COMPRESSEDSTACK - /// <internalonly/> - [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")); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - internal extern IntPtr SetAppDomainStack( SafeCompressedStackHandle csHandle); - - [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - internal extern void RestoreAppDomainStack( IntPtr appDomainStack); - - - /// <internalonly/> - [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 + private extern void StartInternal(ref StackCrawlMark stackMark); // Helper method to get a logical thread ID for StringBuilder (for @@ -366,43 +306,6 @@ namespace System.Threading { 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. - =========================================================================*/ -#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). - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void AbortInternal(); - - public bool Join(TimeSpan timeout) - { - long tm = (long)timeout.TotalMilliseconds; - if (tm < -1 || tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(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. @@ -435,10 +338,8 @@ namespace System.Threading { a explict busy loop because the hardware can be informed that it is busy waiting. */ [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern void SpinWaitInternal(int iterations); - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static new void SpinWait(int iterations) { SpinWaitInternal(iterations); @@ -446,17 +347,14 @@ namespace System.Threading { [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern bool YieldInternal(); - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - public static new bool Yield() + internal static new bool Yield() { return YieldInternal(); } public static new Thread CurrentThread { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] get { Contract.Ensures(Contract.Result<Thread>() != null); return GetCurrentThreadNative(); @@ -467,8 +365,7 @@ namespace System.Threading { private void SetStartHelper(Delegate start, int maxStackSize) { - // We only support default stacks in CoreCLR - Debug.Assert(maxStackSize == 0); + Debug.Assert(maxStackSize >= 0); ThreadHelper threadStartCallBack = new ThreadHelper(start); if(start is ThreadStart) @@ -481,10 +378,6 @@ namespace System.Threading { } } - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern ulong GetProcessDefaultStackSize(); - /*========================================================================= ** PRIVATE Sets the IThreadable interface for the thread. Assumes that ** start != null. @@ -495,122 +388,21 @@ namespace System.Threading { /*========================================================================= ** Clean up the thread when it goes away. =========================================================================*/ - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] ~Thread() { // Delegate to the unmanaged portion. InternalFinalize(); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern void InternalFinalize(); #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 - { - get - { - return (ApartmentState)GetApartmentStateNative(); - } - - set - { - SetApartmentStateNative((int)value, true); - } - } - - public void SetApartmentState(ApartmentState state) - { - bool result = SetApartmentStateHelper(state, true); - if (!result) - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ApartmentStateSwitchFailed")); - } [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. - =========================================================================*/ - 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. - =========================================================================*/ - 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. - =========================================================================*/ - 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. - =========================================================================*/ - 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. - =========================================================================*/ - 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. - =========================================================================*/ - 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: @@ -623,10 +415,6 @@ namespace System.Threading { // - 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). @@ -636,10 +424,6 @@ namespace System.Threading { // 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 - [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 @@ -680,16 +464,8 @@ namespace System.Threading { // 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) { @@ -708,8 +484,6 @@ namespace System.Threading { } } -#if FEATURE_LEAK_CULTURE_INFO -#endif internal CultureInfo GetCurrentUICultureNoAppX() { Contract.Ensures(Contract.Result<CultureInfo>() != null); @@ -725,25 +499,11 @@ namespace System.Threading { 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 - [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 @@ -772,8 +532,6 @@ namespace System.Threading { } } -#if FEATURE_LEAK_CULTURE_INFO -#endif set { if (null==value) { throw new ArgumentNullException(nameof(value)); @@ -783,16 +541,8 @@ namespace System.Threading { // 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) { @@ -810,8 +560,6 @@ namespace System.Threading { } } -#if FEATURE_LEAK_CULTURE_INFO -#endif private CultureInfo GetCurrentCultureNoAppX() { #if FEATURE_COREFX_GLOBALIZATION @@ -826,25 +574,13 @@ namespace System.Threading { 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 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] private static extern void nativeInitCultureAccessors(); -#endif /*====================================================================== ** Returns the current domain in which current thread is running. @@ -855,7 +591,7 @@ namespace System.Threading { [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern AppDomain GetFastDomainInternal(); - public static AppDomain GetDomain() + internal static AppDomain GetDomain() { Contract.Ensures(Contract.Result<AppDomain>() != null); @@ -872,7 +608,7 @@ namespace System.Threading { /* * This returns a unique id to identify an appdomain. */ - public static int GetDomainID() + internal static int GetDomainID() { return GetDomain().GetId(); } @@ -899,267 +635,9 @@ namespace System.Threading { [SuppressUnmanagedCodeSecurity] private static extern void InformThreadNameChange(ThreadHandle t, String name, int len); - internal Object AbortReason { - get { - object result = null; - try - { - result = GetAbortReason(); - } - catch (Exception e) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ExceptionStateCrossAppDomain"), e); - } - return result; - } - set { SetAbortReason(value); } - } - - /*========================================================================= - ** 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; - } - [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; - } - } - - // 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). - [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. - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern Object GetAbortReason(); - - // Helper function to clear the AbortReason. Takes care of - // AppDomain related cleanup if required. - [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 diff --git a/src/mscorlib/src/System/Threading/ThreadAbortException.cs b/src/mscorlib/src/System/Threading/ThreadAbortException.cs index 09ad4e1bd6..25925048bf 100644 --- a/src/mscorlib/src/System/Threading/ThreadAbortException.cs +++ b/src/mscorlib/src/System/Threading/ThreadAbortException.cs @@ -21,7 +21,6 @@ namespace System.Threading using System.Runtime.Serialization; using System.Runtime.CompilerServices; - [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public sealed class ThreadAbortException : SystemException { @@ -36,10 +35,5 @@ namespace System.Threading : base(info, context) { } - - public Object ExceptionState - { - get {return Thread.CurrentThread.AbortReason;} - } } } diff --git a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs index 0056611955..71c09649e2 100644 --- a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs +++ b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs @@ -17,7 +17,6 @@ namespace System.Threading { using System; using System.Runtime.Serialization; - [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public class ThreadInterruptedException : SystemException { public ThreadInterruptedException() diff --git a/src/mscorlib/src/System/Threading/ThreadLocal.cs b/src/mscorlib/src/System/Threading/ThreadLocal.cs index 2b996cb34d..eedf6d0c81 100644 --- a/src/mscorlib/src/System/Threading/ThreadLocal.cs +++ b/src/mscorlib/src/System/Threading/ThreadLocal.cs @@ -16,7 +16,6 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Collections.Generic; -using System.Security.Permissions; using System.Diagnostics; using System.Diagnostics.Contracts; diff --git a/src/mscorlib/src/System/Threading/ThreadPool.cs b/src/mscorlib/src/System/Threading/ThreadPool.cs index 451b15d22f..adf0615819 100644 --- a/src/mscorlib/src/System/Threading/ThreadPool.cs +++ b/src/mscorlib/src/System/Threading/ThreadPool.cs @@ -11,36 +11,20 @@ ** =============================================================================*/ -#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. - */ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Diagnostics.Tracing; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Win32; namespace System.Threading { - using System.Security; - using System.Security.Permissions; - using System; - using Microsoft.Win32; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Runtime.InteropServices; - using System.Collections.Generic; - using System.Diagnostics; - 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 @@ -55,78 +39,76 @@ namespace System.Threading public static bool enableWorkerTracking; public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue(); - - static ThreadPoolGlobals() - { - } } internal sealed class ThreadPoolWorkQueue { - // Simple sparsely populated array to allow lock-free reading. - internal class SparseArray<T> where T : class + internal static class WorkStealingQueueList { - private volatile T[] m_array; + private static volatile WorkStealingQueue[] _queues = new WorkStealingQueue[0]; - internal SparseArray(int initialSize) - { - m_array = new T[initialSize]; - } + public static WorkStealingQueue[] Queues => _queues; - internal T[] Current - { - get { return m_array; } - } - - internal int Add(T e) + public static void Add(WorkStealingQueue queue) { + Debug.Assert(queue != null); while (true) { - T[] array = m_array; - lock (array) + WorkStealingQueue[] oldQueues = _queues; + Debug.Assert(Array.IndexOf(oldQueues, queue) == -1); + + var newQueues = new WorkStealingQueue[oldQueues.Length + 1]; + Array.Copy(oldQueues, 0, newQueues, 0, oldQueues.Length); + newQueues[newQueues.Length - 1] = queue; + if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues) { - 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; - } - } + break; } } } - internal void Remove(T e) + public static void Remove(WorkStealingQueue queue) { - T[] array = m_array; - lock (array) + Debug.Assert(queue != null); + while (true) { - for (int i = 0; i < m_array.Length; i++) + WorkStealingQueue[] oldQueues = _queues; + if (oldQueues.Length == 0) { - if (m_array[i] == e) - { - Volatile.Write(ref m_array[i], null); - break; - } + return; + } + + int pos = Array.IndexOf(oldQueues, queue); + if (pos == -1) + { + Debug.Fail("Should have found the queue"); + return; + } + + var newQueues = new WorkStealingQueue[oldQueues.Length - 1]; + if (pos == 0) + { + Array.Copy(oldQueues, 1, newQueues, 0, newQueues.Length); + } + else if (pos == oldQueues.Length - 1) + { + Array.Copy(oldQueues, 0, newQueues, 0, newQueues.Length); + } + else + { + Array.Copy(oldQueues, 0, newQueues, 0, pos); + Array.Copy(oldQueues, pos + 1, newQueues, pos, newQueues.Length - pos); + } + + if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues) + { + break; } } } } - internal class WorkStealingQueue + internal sealed class WorkStealingQueue { private const int INITIAL_SIZE = 32; internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE]; @@ -142,7 +124,7 @@ namespace System.Threading private volatile int m_headIndex = START_INDEX; private volatile int m_tailIndex = START_INDEX; - private SpinLock m_foreignLock = new SpinLock(false); + private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking:false); public void LocalPush(IThreadPoolWorkItem obj) { @@ -176,7 +158,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(true); + m_foreignLock.Exit(useMemoryBarrier:true); } } @@ -201,7 +183,7 @@ namespace System.Threading if (count >= m_mask) { // We're full; expand the queue by doubling its size. - IThreadPoolWorkItem[] newArray = new IThreadPoolWorkItem[m_array.Length << 1]; + var newArray = new IThreadPoolWorkItem[m_array.Length << 1]; for (int i = 0; i < m_array.Length; i++) newArray[i] = m_array[(i + head) & m_mask]; @@ -218,7 +200,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier:false); } } } @@ -229,13 +211,9 @@ namespace System.Threading // 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)) - { - Debug.Assert(unused == obj); - return true; - } - return false; + IThreadPoolWorkItem unused = LocalPop(); + Debug.Assert(unused == null || unused == obj); + return unused != null; } // Else, do an O(N) search for the work item. The theory of work stealing and our @@ -276,7 +254,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier:false); } } } @@ -284,19 +262,20 @@ namespace System.Threading return false; } + public IThreadPoolWorkItem LocalPop() => m_headIndex < m_tailIndex ? LocalPopCore() : null; + [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")] - public bool LocalPop(out IThreadPoolWorkItem obj) + private IThreadPoolWorkItem LocalPopCore() { 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; + return null; } + // Decrement the tail using a fence to ensure subsequent read doesn't come before. tail -= 1; Interlocked.Exchange(ref m_tailIndex, tail); @@ -304,13 +283,13 @@ namespace System.Threading if (m_headIndex <= tail) { int idx = tail & m_mask; - obj = Volatile.Read(ref m_array[idx]); + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); // Check for nulls in the array. if (obj == null) continue; m_array[idx] = null; - return true; + return obj; } else { @@ -324,241 +303,93 @@ namespace System.Threading { // Element still available. Take it. int idx = tail & m_mask; - obj = Volatile.Read(ref m_array[idx]); + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); // Check for nulls in the array. if (obj == null) continue; m_array[idx] = null; - return true; + return obj; } else { // If we encountered a race condition and element was stolen, restore the tail. m_tailIndex = tail + 1; - obj = null; - return false; + return null; } } finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier:false); } } } } - public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal) - { - return TrySteal(out obj, ref missedSteal, 0); // no blocking by default. - } + public bool CanSteal => m_headIndex < m_tailIndex; - private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout) + public IThreadPoolWorkItem TrySteal(ref bool missedSteal) { - obj = null; - while (true) { - if (m_headIndex >= m_tailIndex) - return false; - - bool taken = false; - try + if (CanSteal) { - m_foreignLock.TryEnter(millisecondsTimeout, ref taken); - if (taken) + bool taken = false; + try { - // 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) + m_foreignLock.TryEnter(ref taken); + if (taken) { - int idx = head & m_mask; - obj = Volatile.Read(ref m_array[idx]); + // Increment head, and ensure read of tail doesn't move before it (fence). + int head = m_headIndex; + Interlocked.Exchange(ref m_headIndex, head + 1); - // Check for nulls in the array. - if (obj == null) continue; + if (head < m_tailIndex) + { + int idx = head & m_mask; + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); - m_array[idx] = null; - return true; - } - else - { - // Failed, restore head. - m_headIndex = head; - obj = null; - missedSteal = true; + // Check for nulls in the array. + if (obj == null) continue; + + m_array[idx] = null; + return obj; + } + else + { + // Failed, restore head. + m_headIndex = head; + } } } - else + finally { - missedSteal = true; + if (taken) + m_foreignLock.Exit(useMemoryBarrier:false); } - } - 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; - - Debug.Assert(upper >= lower); - Debug.Assert(upper <= nodes.Length); - Debug.Assert(lower <= nodes.Length); - Debug.Assert(upper >= 0); - Debug.Assert(lower >= 0); - } - bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower) - { - Debug.Assert(newUpper >= newLower); - Debug.Assert(newUpper <= nodes.Length); - Debug.Assert(newLower <= nodes.Length); - Debug.Assert(newUpper >= 0); - Debug.Assert(newLower >= 0); - Debug.Assert(newUpper >= prevUpper); - Debug.Assert(newLower >= prevLower); - Debug.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() - { - Debug.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. - // - Debug.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)) - { - Debug.Assert(Volatile.Read(ref nodes[upper]) == null); - Volatile.Write(ref nodes[upper], node); - return true; + missedSteal = 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; - } + return null; } } } - // 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 readonly SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16); + internal readonly ConcurrentQueue<IThreadPoolWorkItem> workItems = new ConcurrentQueue<IThreadPoolWorkItem>(); private volatile int numOutstandingThreadRequests = 0; public ThreadPoolWorkQueue() { - queueTail = queueHead = new QueueSegment(); loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer); } - public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() - { - if (null == ThreadPoolWorkQueueThreadLocals.threadLocals) - ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this); - return ThreadPoolWorkQueueThreadLocals.threadLocals; - } + public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() => + ThreadPoolWorkQueueThreadLocals.threadLocals ?? + (ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this)); internal void EnsureThreadRequested() { @@ -602,12 +433,12 @@ namespace System.Threading public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal) { + if (loggingEnabled) + System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback); + ThreadPoolWorkQueueThreadLocals tl = null; if (!forceGlobal) tl = ThreadPoolWorkQueueThreadLocals.threadLocals; - - if (loggingEnabled) - System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback); if (null != tl) { @@ -615,18 +446,7 @@ namespace System.Threading } 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; - } - } + workItems.Enqueue(callback); } EnsureThreadRequested(); @@ -635,67 +455,43 @@ namespace System.Threading internal bool LocalFindAndPop(IThreadPoolWorkItem callback) { ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals; - if (null == tl) - return false; - - return tl.workStealingQueue.LocalFindAndPop(callback); + return tl != null && tl.workStealingQueue.LocalFindAndPop(callback); } - public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal) + public IThreadPoolWorkItem Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal) { - callback = null; - missedSteal = false; - WorkStealingQueue wsq = tl.workStealingQueue; + WorkStealingQueue localWsq = tl.workStealingQueue; + IThreadPoolWorkItem callback; - if (wsq.LocalPop(out callback)) - Debug.Assert(null != callback); - - if (null == callback) + if ((callback = localWsq.LocalPop()) == null && // first try the local queue + !workItems.TryDequeue(out callback)) // then try the global queue { - QueueSegment tail = queueTail; - while (true) - { - if (tail.TryDequeue(out callback)) - { - Debug.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; + // finally try to steal from another thread's local queue + WorkStealingQueue[] queues = WorkStealingQueueList.Queues; + int c = queues.Length; + Debug.Assert(c > 0, "There must at least be a queue for this thread."); 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)) + WorkStealingQueue otherQueue = queues[i]; + if (otherQueue != localWsq && otherQueue.CanSteal) { - Debug.Assert(null != callback); - break; + callback = otherQueue.TrySteal(ref missedSteal); + if (callback != null) + { + break; + } } c--; } } + + return callback; } - static internal bool Dispatch() + internal static bool Dispatch() { var workQueue = ThreadPoolGlobals.workQueue; // @@ -735,85 +531,66 @@ namespace System.Threading // while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.TP_QUANTUM) { - // - // 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(); - } - } + bool missedSteal = false; + workItem = workQueue.Dequeue(tl, ref 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; + // 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) + if (workQueue.loggingEnabled) + System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem); + + // + // 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(); + + // + // Execute the workitem outside of any finally blocks, so that it can be aborted if needed. + // + if (ThreadPoolGlobals.enableWorkerTracking) + { + bool reportedStatus = false; + try { - bool reportedStatus = false; - try - { - try { } - finally - { - ThreadPool.ReportThreadStatus(true); - reportedStatus = true; - } - workItem.ExecuteWorkItem(); - workItem = null; - } - finally - { - if (reportedStatus) - ThreadPool.ReportThreadStatus(false); - } + ThreadPool.ReportThreadStatus(isWorking: true); + reportedStatus = true; + workItem.ExecuteWorkItem(); } - else + finally { - workItem.ExecuteWorkItem(); - workItem = null; + if (reportedStatus) + ThreadPool.ReportThreadStatus(isWorking: false); } - - // - // 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; } + 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; } @@ -825,8 +602,7 @@ namespace System.Threading // 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); + workItem?.MarkAborted(tae); // // In this case, the VM is going to request another thread on our behalf. No need to do it twice. @@ -845,11 +621,36 @@ namespace System.Threading } // 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. - Debug.Assert(false); + Debug.Fail("Should never reach this point"); return true; } } + // Simple random number generator. We don't need great randomness, we just need a little and for it to be fast. + internal struct FastRandom // xorshift prng + { + private uint _w, _x, _y, _z; + + public FastRandom(int seed) + { + _x = (uint)seed; + _w = 88675123; + _y = 362436069; + _z = 521288629; + } + + public int Next(int maxValue) + { + Debug.Assert(maxValue > 0); + + uint t = _x ^ (_x << 11); + _x = _y; _y = _z; _z = _w; + _w = _w ^ (_w >> 19) ^ (t ^ (t >> 8)); + + return (int)(_w % (uint)maxValue); + } + } + // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened. internal sealed class ThreadPoolWorkQueueThreadLocals { @@ -858,13 +659,13 @@ namespace System.Threading public readonly ThreadPoolWorkQueue workQueue; public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue; - public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId); + public FastRandom random = new FastRandom(Thread.CurrentThread.ManagedThreadId); // mutable struct, do not copy or make readonly public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) { workQueue = tpq; workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); - ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue); + ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue); } private void CleanUp() @@ -873,28 +674,15 @@ namespace System.Threading { if (null != workQueue) { - bool done = false; - while (!done) + IThreadPoolWorkItem cb; + while ((cb = workStealingQueue.LocalPop()) != null) { - // Ensure that we won't be aborted between LocalPop and Enqueue. - try { } - finally - { - IThreadPoolWorkItem cb = null; - if (workStealingQueue.LocalPop(out cb)) - { - Debug.Assert(null != cb); - workQueue.Enqueue(cb, true); - } - else - { - done = true; - } - } + Debug.Assert(null != cb); + workQueue.Enqueue(cb, forceGlobal: true); } } - ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue); + ThreadPoolWorkQueue.WorkStealingQueueList.Remove(workStealingQueue); } } @@ -912,34 +700,19 @@ namespace System.Threading internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject { - private static IntPtr InvalidHandle - { - get - { - return Win32Native.INVALID_HANDLE_VALUE; - } - } - private IntPtr registeredWaitHandle; + private static IntPtr InvalidHandle => Win32Native.INVALID_HANDLE_VALUE; + private IntPtr registeredWaitHandle = InvalidHandle; private WaitHandle m_internalWaitObject; private bool bReleaseNeeded = false; private volatile int m_lock = 0; - internal RegisteredWaitHandleSafe() - { - registeredWaitHandle = InvalidHandle; - } - - internal IntPtr GetHandle() - { - return registeredWaitHandle; - } + internal IntPtr GetHandle() => registeredWaitHandle; internal void SetHandle(IntPtr handle) { registeredWaitHandle = handle; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal void SetWaitObject(WaitHandle waitObject) { // needed for DangerousAddRef @@ -957,7 +730,6 @@ namespace System.Threading } } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal bool Unregister( WaitHandle waitObject // object to be notified when all callbacks to delegates have completed ) @@ -1009,10 +781,8 @@ namespace System.Threading return result; } - private bool ValidHandle() - { - return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero); - } + private bool ValidHandle() => + registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero; ~RegisteredWaitHandleSafe() { @@ -1071,9 +841,8 @@ namespace System.Threading private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject); } -[System.Runtime.InteropServices.ComVisible(true)] public sealed class RegisteredWaitHandle : MarshalByRefObject { - private RegisteredWaitHandleSafe internalRegisteredWait; + private readonly RegisteredWaitHandleSafe internalRegisteredWait; internal RegisteredWaitHandle() { @@ -1090,8 +859,6 @@ namespace System.Threading internalRegisteredWait.SetWaitObject(waitObject); } - -[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 @@ -1101,10 +868,8 @@ namespace System.Threading } } - [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 // @@ -1115,10 +880,7 @@ namespace System.Threading // internal static class _ThreadPoolWaitCallback { - static internal bool PerformWaitCallback() - { - return ThreadPoolWorkQueue.Dispatch(); - } + internal static bool PerformWaitCallback() => ThreadPoolWorkQueue.Dispatch(); } // @@ -1138,11 +900,9 @@ namespace System.Threading internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem { - static QueueUserWorkItemCallback() {} - private WaitCallback callback; - private ExecutionContext context; - private Object state; + private readonly ExecutionContext context; + private readonly Object state; #if DEBUG volatile int executed; @@ -1173,7 +933,7 @@ namespace System.Threading void IThreadPoolWorkItem.ExecuteWorkItem() { #if DEBUG - MarkExecuted(false); + MarkExecuted(aborted:false); #endif // call directly if it is an unsafe call OR EC flow is suppressed if (context == null) @@ -1184,7 +944,7 @@ namespace System.Threading } else { - ExecutionContext.Run(context, ccb, this, true); + ExecutionContext.Run(context, ccb, this); } } @@ -1193,16 +953,16 @@ namespace System.Threading #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); + MarkExecuted(aborted:true); #endif } - static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context); + internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context); - static private void WaitCallback_Context(Object state) + private static void WaitCallback_Context(Object state) { QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state; - WaitCallback wc = obj.callback as WaitCallback; + WaitCallback wc = obj.callback; Debug.Assert(null != wc); wc(obj.state); } @@ -1210,10 +970,8 @@ namespace System.Threading internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem { - static QueueUserWorkItemCallbackDefaultContext() { } - private WaitCallback callback; - private Object state; + private readonly Object state; #if DEBUG private volatile int executed; @@ -1243,9 +1001,9 @@ namespace System.Threading void IThreadPoolWorkItem.ExecuteWorkItem() { #if DEBUG - MarkExecuted(false); + MarkExecuted(aborted:false); #endif - ExecutionContext.Run(ExecutionContext.PreAllocatedDefault, ccb, this, true); + ExecutionContext.Run(ExecutionContext.Default, ccb, this); } void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) @@ -1253,16 +1011,16 @@ namespace System.Threading #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); + MarkExecuted(aborted:true); #endif } - static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context); + internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context); - static private void WaitCallback_Context(Object state) + private static void WaitCallback_Context(Object state) { QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state; - WaitCallback wc = obj.callback as WaitCallback; + WaitCallback wc = obj.callback; Debug.Assert(null != wc); obj.callback = null; wc(obj.state); @@ -1271,46 +1029,38 @@ namespace System.Threading internal class _ThreadPoolWaitOrTimerCallback { - static _ThreadPoolWaitOrTimerCallback() {} - WaitOrTimerCallback _waitOrTimerCallback; ExecutionContext _executionContext; Object _state; - static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t); - static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f); + private static readonly ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t); + private static readonly ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f); - internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark) + internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack) { _waitOrTimerCallback = waitOrTimerCallback; _state = state; - if (compressStack && !ExecutionContext.IsFlowSuppressed()) + if (compressStack) { // capture the exection context - _executionContext = ExecutionContext.Capture( - ref stackMark, - ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase); + _executionContext = ExecutionContext.Capture(); } } - static private void WaitOrTimerCallback_Context_t(Object state) - { - WaitOrTimerCallback_Context(state, true); - } + private static void WaitOrTimerCallback_Context_t(Object state) => + WaitOrTimerCallback_Context(state, timedOut:true); - static private void WaitOrTimerCallback_Context_f(Object state) - { - WaitOrTimerCallback_Context(state, false); - } + private static void WaitOrTimerCallback_Context_f(Object state) => + WaitOrTimerCallback_Context(state, timedOut:false); - static private void WaitOrTimerCallback_Context(Object state, bool timedOut) + private static void WaitOrTimerCallback_Context(Object state, bool timedOut) { _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; helper._waitOrTimerCallback(helper._state, timedOut); } // call back helper - static internal void PerformWaitOrTimerCallback(Object state, bool timedOut) + internal static void PerformWaitOrTimerCallback(Object state, bool timedOut) { _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; Debug.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!"); @@ -1322,20 +1072,13 @@ namespace System.Threading } else { - using (ExecutionContext executionContext = helper._executionContext.CreateCopy()) - { - if (timedOut) - ExecutionContext.Run(executionContext, _ccbt, helper, true); - else - ExecutionContext.Run(executionContext, _ccbf, helper, true); - } + ExecutionContext.Run(helper._executionContext, timedOut ? _ccbt : _ccbf, helper); } } } [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 @@ -1343,7 +1086,6 @@ namespace System.Threading public static class ThreadPool { - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { return SetMaxThreadsNative(workerThreads, completionPortThreads); @@ -1412,7 +1154,7 @@ namespace System.Threading if (callBack != null) { - _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark); + _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack); 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 @@ -1533,141 +1275,84 @@ namespace System.Threading StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false); } - - [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); - } - - [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); - } - - [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. - private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack ) - { - bool success = true; + public static bool QueueUserWorkItem(WaitCallback callBack) => + QueueUserWorkItem(callBack, null); - if (callBack != null) + public static bool QueueUserWorkItem(WaitCallback callBack, object state) + { + 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. + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); + } - EnsureVMInitialized(); + 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; + ExecutionContext context = ExecutionContext.Capture(); - IThreadPoolWorkItem tpcallBack = context == ExecutionContext.PreAllocatedDefault ? - new QueueUserWorkItemCallbackDefaultContext(callBack, state) : - (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context); + IThreadPoolWorkItem tpcallBack = context == ExecutionContext.Default ? + new QueueUserWorkItemCallbackDefaultContext(callBack, state) : + (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context); - ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, true); - success = true; - } - } - else + ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true); + + return true; + } + + public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, Object state) + { + if (callBack == null) { - throw new ArgumentNullException(nameof(WaitCallback)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); } - return success; + + EnsureVMInitialized(); + + IThreadPoolWorkItem tpcallBack = new QueueUserWorkItemCallback(callBack, state, null); + + ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true); + + return true; } internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal) { Debug.Assert(null != workItem); EnsureVMInitialized(); - - // - // Enqueue needs to be protected from ThreadAbort - // - try { } - finally - { - ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal); - } + ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal); } // This method tries to take the target callback out of the current thread's queue. internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem) { Debug.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); + return + ThreadPoolGlobals.vmTpInitialized && // if not initialized, so there's no way this workitem was ever queued. + ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem); } // Get all workitems. Called by TaskScheduler in its debugger hooks. 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) + // Enumerate global queue + foreach (IThreadPoolWorkItem workItem in ThreadPoolGlobals.workQueue.workItems) { - // 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; - } - } - } + yield return workItem; } - if (globalQueueTail != null) + // Enumerate each local queue + foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in ThreadPoolWorkQueue.WorkStealingQueueList.Queues) { - // Now the global queue - for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail; - segment != null; - segment = segment.Next) + if (wsq != null && wsq.m_array != null) { - IThreadPoolWorkItem[] items = segment.nodes; + IThreadPoolWorkItem[] items = wsq.m_array; for (int i = 0; i < items.Length; i++) { IThreadPoolWorkItem item = items[i]; if (item != null) + { yield return item; + } } } } @@ -1675,13 +1360,20 @@ namespace System.Threading internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems() { - return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null); + ThreadPoolWorkQueue.WorkStealingQueue wsq = ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue; + 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; + } + } } - internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems() - { - return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail); - } + internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems() => ThreadPoolGlobals.workQueue.workItems; private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems) { @@ -1705,20 +1397,14 @@ namespace System.Threading // 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. - internal static object[] GetQueuedWorkItemsForDebugger() - { - return ToObjectArray(GetQueuedWorkItems()); - } + internal static object[] GetQueuedWorkItemsForDebugger() => + ToObjectArray(GetQueuedWorkItems()); - internal static object[] GetGloballyQueuedWorkItemsForDebugger() - { - return ToObjectArray(GetGloballyQueuedWorkItems()); - } + internal static object[] GetGloballyQueuedWorkItemsForDebugger() => + ToObjectArray(GetGloballyQueuedWorkItems()); - internal static object[] GetLocallyQueuedWorkItemsForDebugger() - { - return ToObjectArray(GetLocallyQueuedWorkItems()); - } + internal static object[] GetLocallyQueuedWorkItemsForDebugger() => + ToObjectArray(GetLocallyQueuedWorkItems()); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] @@ -1728,19 +1414,26 @@ namespace System.Threading unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped); [CLSCompliant(false)] - unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) - { - return PostQueuedCompletionStatus(overlapped); - } + unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => + PostQueuedCompletionStatus(overlapped); + // 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. private static void EnsureVMInitialized() { if (!ThreadPoolGlobals.vmTpInitialized) { - ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); - ThreadPoolGlobals.vmTpInitialized = true; + EnsureVMInitializedCore(); // separate out to help with inlining } } + + private static void EnsureVMInitializedCore() + { + ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); + ThreadPoolGlobals.vmTpInitialized = true; + } // Native methods: @@ -1795,9 +1488,7 @@ namespace System.Threading [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] - public static bool BindHandle( - IntPtr osHandle - ) + public static bool BindHandle(IntPtr osHandle) { return BindIOCompletionCallbackNative(osHandle); } @@ -1822,7 +1513,6 @@ namespace System.Threading } [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 index c56156eb89..6303c2fd94 100644 --- a/src/mscorlib/src/System/Threading/ThreadPriority.cs +++ b/src/mscorlib/src/System/Threading/ThreadPriority.cs @@ -16,7 +16,6 @@ namespace System.Threading { using System.Threading; [Serializable] -[System.Runtime.InteropServices.ComVisible(true)] public enum ThreadPriority { /*========================================================================= diff --git a/src/mscorlib/src/System/Threading/ThreadStart.cs b/src/mscorlib/src/System/Threading/ThreadStart.cs index b968117195..e4beddcd75 100644 --- a/src/mscorlib/src/System/Threading/ThreadStart.cs +++ b/src/mscorlib/src/System/Threading/ThreadStart.cs @@ -14,12 +14,10 @@ =============================================================================*/ 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/ThreadState.cs b/src/mscorlib/src/System/Threading/ThreadState.cs index 007e1bf6e9..2d953f384a 100644 --- a/src/mscorlib/src/System/Threading/ThreadState.cs +++ b/src/mscorlib/src/System/Threading/ThreadState.cs @@ -16,7 +16,6 @@ namespace System.Threading { [Serializable] [Flags] -[System.Runtime.InteropServices.ComVisible(true)] public enum ThreadState { /*========================================================================= diff --git a/src/mscorlib/src/System/Threading/ThreadStateException.cs b/src/mscorlib/src/System/Threading/ThreadStateException.cs index 535dffcdbf..97c03ce06c 100644 --- a/src/mscorlib/src/System/Threading/ThreadStateException.cs +++ b/src/mscorlib/src/System/Threading/ThreadStateException.cs @@ -16,7 +16,6 @@ namespace System.Threading { using System; using System.Runtime.Serialization; - [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public class ThreadStateException : SystemException { public ThreadStateException() diff --git a/src/mscorlib/src/System/Threading/Timeout.cs b/src/mscorlib/src/System/Threading/Timeout.cs index 99e24159b2..80bdbccf4e 100644 --- a/src/mscorlib/src/System/Threading/Timeout.cs +++ b/src/mscorlib/src/System/Threading/Timeout.cs @@ -8,10 +8,8 @@ namespace System.Threading { // 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; diff --git a/src/mscorlib/src/System/Threading/Timer.cs b/src/mscorlib/src/System/Threading/Timer.cs index 5bfefccad2..93d2922799 100644 --- a/src/mscorlib/src/System/Threading/Timer.cs +++ b/src/mscorlib/src/System/Threading/Timer.cs @@ -8,7 +8,6 @@ namespace System.Threading { using System; using System.Security; - using System.Security.Permissions; using Microsoft.Win32; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -21,7 +20,6 @@ namespace System.Threading - [System.Runtime.InteropServices.ComVisible(true)] public delegate void TimerCallback(Object state); // @@ -109,7 +107,6 @@ namespace System.Threading { } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override bool ReleaseHandle() { return DeleteAppDomainTimer(handle); @@ -206,7 +203,6 @@ namespace System.Threading [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] static extern bool DeleteAppDomainTimer(IntPtr handle); #endregion @@ -221,83 +217,6 @@ namespace System.Threading volatile int m_pauseTicks = 0; // Time when Pause was called - 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; - } - } - } - - 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) - { - Debug.Assert(timer.m_dueTime != Timeout.UnsignedInfinite); - Debug.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. @@ -512,19 +431,13 @@ namespace System.Threading volatile WaitHandle m_notifyWhenNoCallbacksRunning; - internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark) + internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period) { 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); - } + m_executionContext = ExecutionContext.Capture(); // // After the following statement, the timer may fire. No more manipulation of timer state outside of @@ -678,29 +591,15 @@ namespace System.Threading } 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 - } + ExecutionContext.Run(m_executionContext, s_callCallbackInContext, this); } } - private static ContextCallback s_callCallbackInContext; - - private static void CallCallbackInContext(object state) + private static readonly ContextCallback s_callCallbackInContext = state => { TimerQueueTimer t = (TimerQueueTimer)state; t.m_timerCallback(t.m_state); - } + }; } // @@ -756,14 +655,12 @@ namespace System.Threading } - [System.Runtime.InteropServices.ComVisible(true)] public sealed class Timer : MarshalByRefObject, IDisposable { private const UInt32 MAX_SUPPORTED_TIMEOUT = (uint)0xfffffffe; private TimerHolder m_timer; - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable public Timer(TimerCallback callback, Object state, int dueTime, @@ -774,12 +671,10 @@ namespace System.Threading if (period < -1 ) throw new ArgumentOutOfRangeException(nameof(period), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period,ref stackMark); + TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable public Timer(TimerCallback callback, Object state, TimeSpan dueTime, @@ -797,22 +692,18 @@ namespace System.Threading if (periodTm > MAX_SUPPORTED_TIMEOUT) throw new ArgumentOutOfRangeException(nameof(periodTm),Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge")); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm,ref stackMark); + TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm); } [CLSCompliant(false)] - [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); + TimerSetup(callback,state,dueTime,period); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable public Timer(TimerCallback callback, Object state, long dueTime, @@ -827,11 +718,9 @@ namespace System.Threading if (period > MAX_SUPPORTED_TIMEOUT) throw new ArgumentOutOfRangeException(nameof(period),Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge")); Contract.EndContractBlock(); - StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - TimerSetup(callback,state,(UInt32) dueTime, (UInt32) period,ref stackMark); + TimerSetup(callback,state,(UInt32) dueTime, (UInt32) period); } - [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 @@ -839,31 +728,19 @@ namespace System.Threading // 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); + TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period); } private void TimerSetup(TimerCallback callback, Object state, UInt32 dueTime, - UInt32 period, - ref StackCrawlMark stackMark) + UInt32 period) { if (callback == null) throw new ArgumentNullException(nameof(TimerCallback)); Contract.EndContractBlock(); - m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, ref stackMark)); - } - - internal static void Pause() - { - TimerQueue.Instance.Pause(); - } - - internal static void Resume() - { - TimerQueue.Instance.Resume(); + m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period)); } public bool Change(int dueTime, int period) diff --git a/src/mscorlib/src/System/Threading/Volatile.cs b/src/mscorlib/src/System/Threading/Volatile.cs index 3894b435fa..c94a69ab7b 100644 --- a/src/mscorlib/src/System/Threading/Volatile.cs +++ b/src/mscorlib/src/System/Threading/Volatile.cs @@ -26,7 +26,6 @@ namespace System.Threading // public static class Volatile { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static bool Read(ref bool location) { @@ -38,7 +37,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static sbyte Read(ref sbyte location) @@ -51,7 +49,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static byte Read(ref byte location) { @@ -63,7 +60,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static short Read(ref short location) { @@ -75,7 +71,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static ushort Read(ref ushort location) @@ -88,7 +83,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static int Read(ref int location) { @@ -100,7 +94,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static uint Read(ref uint location) @@ -114,7 +107,6 @@ namespace System.Threading } #if BIT64 - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static long Read(ref long location) { @@ -126,7 +118,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static ulong Read(ref ulong location) @@ -139,7 +130,6 @@ namespace System.Threading return value; } #else - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static long Read(ref long location) { // @@ -151,7 +141,6 @@ namespace System.Threading return Interlocked.CompareExchange(ref location, 0, 0); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] public static ulong Read(ref ulong location) { @@ -169,7 +158,6 @@ namespace System.Threading } #endif - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static IntPtr Read(ref IntPtr location) { @@ -181,7 +169,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static UIntPtr Read(ref UIntPtr location) @@ -194,7 +181,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static float Read(ref float location) { @@ -206,7 +192,6 @@ namespace System.Threading return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static double Read(ref double location) { @@ -219,7 +204,6 @@ namespace System.Threading return Interlocked.CompareExchange(ref location, 0, 0); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static T Read<T>(ref T location) where T : class { @@ -234,7 +218,6 @@ namespace System.Threading - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref bool location, bool value) { @@ -245,7 +228,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref sbyte location, sbyte value) @@ -257,7 +239,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref byte location, byte value) { @@ -268,7 +249,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref short location, short value) { @@ -279,7 +259,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref ushort location, ushort value) @@ -291,7 +270,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref int location, int value) { @@ -302,7 +280,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref uint location, uint value) @@ -315,7 +292,6 @@ namespace System.Threading } #if BIT64 - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref long location, long value) { @@ -326,7 +302,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref ulong location, ulong value) @@ -338,7 +313,6 @@ namespace System.Threading location = value; } #else - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static void Write(ref long location, long value) { // @@ -350,7 +324,6 @@ namespace System.Threading Interlocked.Exchange(ref location, value); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] public static void Write(ref ulong location, ulong value) { @@ -374,7 +347,6 @@ namespace System.Threading } #endif - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref IntPtr location, IntPtr value) { @@ -385,7 +357,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [CLSCompliant(false)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref UIntPtr location, UIntPtr value) @@ -397,7 +368,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref float location, float value) { @@ -408,7 +378,6 @@ namespace System.Threading location = value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write(ref double location, double value) { @@ -421,7 +390,6 @@ namespace System.Threading Interlocked.Exchange(ref location, value); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [System.Runtime.Versioning.NonVersionable] public static void Write<T>(ref T location, T value) where T : class { diff --git a/src/mscorlib/src/System/Threading/WaitHandle.cs b/src/mscorlib/src/System/Threading/WaitHandle.cs index 7638c8b35b..d4dcd710be 100644 --- a/src/mscorlib/src/System/Threading/WaitHandle.cs +++ b/src/mscorlib/src/System/Threading/WaitHandle.cs @@ -17,7 +17,6 @@ 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; @@ -27,7 +26,6 @@ namespace System.Threading using System.Diagnostics.CodeAnalysis; using Win32Native = Microsoft.Win32.Win32Native; - [System.Runtime.InteropServices.ComVisible(true)] public abstract class WaitHandle : MarshalByRefObject, IDisposable { public const int WaitTimeout = 0x102; @@ -102,7 +100,6 @@ namespace System.Threading public SafeWaitHandle SafeWaitHandle { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] get { if (safeWaitHandle == null) @@ -112,7 +109,6 @@ namespace System.Threading return safeWaitHandle; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] set { // Set safeWaitHandle and waitHandle in a CER so we won't take @@ -245,7 +241,6 @@ namespace System.Threading ========================================================================*/ [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private static extern int WaitMultiple(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext, bool WaitAll); public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) @@ -350,7 +345,6 @@ namespace System.Threading ** (if in a synchronized context) is exited before the wait and reacquired ========================================================================*/ - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) { if (waitHandles==null) @@ -406,7 +400,6 @@ namespace System.Threading return ret; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public static int WaitAny( WaitHandle[] waitHandles, TimeSpan timeout, @@ -419,7 +412,6 @@ namespace System.Threading } return WaitAny(waitHandles,(int)tm, exitContext); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout) { return WaitAny(waitHandles, timeout, true); @@ -429,13 +421,11 @@ namespace System.Threading /*======================================================================== ** 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); @@ -446,10 +436,11 @@ namespace System.Threading == SignalAndWait == ==================================================*/ - +#if !PLATFORM_UNIX [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal,SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout, bool hasThreadAffinity, bool exitContext); +#endif // !PLATFORM_UNIX public static bool SignalAndWait( WaitHandle toSignal, |