diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading/Tasks')
17 files changed, 560 insertions, 748 deletions
diff --git a/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs index ec7c5aaeea..ec154f9efb 100644 --- a/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs +++ b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs @@ -21,7 +21,6 @@ using WFD = Windows.Foundation.Diagnostics; namespace System.Threading.Tasks { - [FriendAccessAllowed] internal enum CausalityTraceLevel { @@ -85,13 +84,13 @@ namespace System.Threading.Tasks [FriendAccessAllowed] internal static class AsyncCausalityTracer { - static internal void EnableToETW(bool enabled) + static internal void EnableToETW(bool enabled) { #if FEATURE_COMINTEROP if (enabled) - f_LoggingOn |= Loggers.ETW; - else - f_LoggingOn &= ~Loggers.ETW; + f_LoggingOn |= Loggers.ETW; + else + f_LoggingOn &= ~Loggers.ETW; #endif } @@ -121,7 +120,8 @@ namespace System.Threading.Tasks // The loggers that this Tracer knows about. [Flags] - private enum Loggers : byte { + private enum Loggers : byte + { CausalityTracer = 1, ETW = 2 } @@ -148,7 +148,7 @@ namespace System.Threading.Tasks int hresult = Microsoft.Win32.UnsafeNativeMethods.RoGetActivationFactory(ClassId, ref guid, out factory); if (hresult < 0 || factory == null) return; //This prevents having an exception thrown in case IAsyncCausalityTracerStatics isn't registered. - + s_TracerFactory = (WFD.IAsyncCausalityTracerStatics)factory; EventRegistrationToken token = s_TracerFactory.add_TracingStatusChanged(new EventHandler<WFD.TracingStatusChangedEventArgs>(TracingStatusChangedHandler)); @@ -161,15 +161,14 @@ namespace System.Threading.Tasks // doing here depends on internal state. LogAndDisable(ex); } - } private static void TracingStatusChangedHandler(Object sender, WFD.TracingStatusChangedEventArgs args) { if (args.Enabled) - f_LoggingOn |= Loggers.CausalityTracer; - else - f_LoggingOn &= ~Loggers.CausalityTracer; + f_LoggingOn |= Loggers.CausalityTracer; + else + f_LoggingOn &= ~Loggers.CausalityTracer; } #endif @@ -185,11 +184,11 @@ namespace System.Threading.Tasks try { if ((f_LoggingOn & Loggers.ETW) != 0) - TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long) relatedContext); + TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long)relatedContext); if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationCreation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), operationName, relatedContext); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -209,7 +208,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.AsyncCausalityStatus)status); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -228,7 +227,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationRelation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalityRelation)relation); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -247,7 +246,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceSynchronousWorkStart((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalitySynchronousWork)work); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -266,7 +265,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceSynchronousWorkCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, (WFD.CausalitySynchronousWork)work); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -288,6 +287,5 @@ namespace System.Threading.Tasks { return (((ulong)AppDomain.CurrentDomain.Id) << 32) + taskId; } - } } diff --git a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs index a87406a493..07a673bf4e 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs @@ -72,22 +72,25 @@ namespace System.Threading.Tasks /// Initializes the ConcurrentExclusiveSchedulerPair. /// </summary> public ConcurrentExclusiveSchedulerPair() : - this(TaskScheduler.Default, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + this(TaskScheduler.Default, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler. /// </summary> /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param> public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler) : - this(taskScheduler, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + this(taskScheduler, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum concurrency level. /// </summary> /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param> /// <param name="maxConcurrencyLevel">The maximum number of tasks to run concurrently.</param> - public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel) : - this(taskScheduler, maxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel) : + this(taskScheduler, maxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum @@ -141,7 +144,7 @@ namespace System.Threading.Tasks } /// <summary>Gets a <see cref="System.Threading.Tasks.Task"/> that will complete when the scheduler has completed processing.</summary> - public Task Completion + public Task Completion { // ValueLock not needed, but it's ok if it's held get { return EnsureCompletionStateInitialized().Task; } @@ -162,7 +165,7 @@ namespace System.Threading.Tasks } /// <summary>Sets that completion has been requested.</summary> - private void RequestCompletion() + private void RequestCompletion() { ContractAssertMonitorStatus(ValueLock, held: true); EnsureCompletionStateInitialized().m_completionRequested = true; @@ -190,9 +193,9 @@ namespace System.Threading.Tasks // Now, only allow shutdown if an exception occurred or if there are no more tasks to process. var cs = EnsureCompletionStateInitialized(); - return + return (cs.m_exceptions != null && cs.m_exceptions.Count > 0) || - (m_concurrentTaskScheduler.m_tasks.IsEmpty && m_exclusiveTaskScheduler.m_tasks.IsEmpty); + (m_concurrentTaskScheduler.m_tasks.IsEmpty && m_exclusiveTaskScheduler.m_tasks.IsEmpty); } } @@ -330,7 +333,7 @@ namespace System.Threading.Tasks } } } - + // Check to see if all tasks have completed and if completion has been requested. CleanupStateIfCompletingAndQuiesced(); } @@ -370,7 +373,7 @@ namespace System.Threading.Tasks // We're no longer processing exclusive tasks on the current thread ProcessingMode currentMode; m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode); - Debug.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask, + Debug.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask, "Somehow we ended up escaping exclusive mode."); lock (ValueLock) @@ -720,7 +723,7 @@ namespace System.Threading.Tasks return mode; } } - + /// <summary>Asserts that a given synchronization object is either held or not held.</summary> /// <param name="syncObj">The monitor to check.</param> /// <param name="held">Whether we want to assert that it's currently held or not held.</param> @@ -748,7 +751,7 @@ namespace System.Threading.Tasks Debug.Assert(Monitor.IsEntered(syncObj) == held, "The locking scheme was not correctly followed."); #endif } - + /// <summary>Gets the options to use for tasks.</summary> /// <param name="isReplacementReplica">If this task is being created to replace another.</param> /// <remarks> @@ -758,7 +761,7 @@ namespace System.Threading.Tasks /// <returns>The options to use.</returns> internal static TaskCreationOptions GetCreationOptionsForTask(bool isReplacementReplica = false) { - TaskCreationOptions options = + TaskCreationOptions options = #if PRENET45 TaskCreationOptions.None; #else @@ -784,5 +787,4 @@ namespace System.Threading.Tasks Completed = 0x8 } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs index 137afa11f5..60a7c81dcf 100644 --- a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs @@ -568,8 +568,6 @@ namespace System.Threading.Tasks promise.DangerousSetResult(result); } } - - } } @@ -691,7 +689,7 @@ namespace System.Threading.Tasks // RespectParentCancellation. Task t = new Task(new Action<object>(delegate { - FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: true); }), (object)null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null); @@ -706,7 +704,7 @@ namespace System.Threading.Tasks if (asyncResult.IsCompleted) { - try { t.InternalRunSynchronously(scheduler, waitForCompletion:false); } + try { t.InternalRunSynchronously(scheduler, waitForCompletion: false); } catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions } else @@ -792,7 +790,7 @@ namespace System.Threading.Tasks ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); - + TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task<TResult> promise = new Task<TResult>(state, creationOptions); @@ -943,7 +941,6 @@ namespace System.Threading.Tasks } catch { - if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); @@ -1241,7 +1238,7 @@ namespace System.Threading.Tasks internal static Task<TResult> FromAsyncTrim<TInstance, TArgs>( TInstance thisRef, TArgs args, Func<TInstance, TArgs, AsyncCallback, object, IAsyncResult> beginMethod, - Func<TInstance, IAsyncResult, TResult> endMethod) + Func<TInstance, IAsyncResult, TResult> endMethod) where TInstance : class { // Validate arguments, but only with asserts, as this is an internal only implementation. @@ -1319,7 +1316,7 @@ namespace System.Threading.Tasks // we'll instead complete the promise at the call site. if (!asyncResult.CompletedSynchronously) { - promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization:true); + promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization: true); } } @@ -1661,7 +1658,7 @@ namespace System.Threading.Tasks return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } - + // Core implementation of ContinueWhenAll -- the generic version // Note: if you make any changes to this method, please do the same to the non-generic version too. internal static Task<TResult> ContinueWhenAllImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks, @@ -1744,10 +1741,10 @@ namespace System.Threading.Tasks //the following delegate avoids closure capture as much as possible //completedTasks.Result == tasksCopy; //state == continuationFunction - (completedTasks, state) => + (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); - return ((Func<Task[], TResult>)state)(completedTasks.Result); + return ((Func<Task[], TResult>)state)(completedTasks.Result); }, continuationFunction, scheduler, cancellationToken, continuationOptions); } @@ -1755,13 +1752,13 @@ namespace System.Threading.Tasks { Debug.Assert(continuationAction != null); return starter.ContinueWith<TResult>( - //the following delegate avoids closure capture as much as possible - //completedTasks.Result == tasksCopy; - //state == continuationAction - (completedTasks, state) => + //the following delegate avoids closure capture as much as possible + //completedTasks.Result == tasksCopy; + //state == continuationAction + (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); - ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); + ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); }, continuationAction, scheduler, cancellationToken, continuationOptions); } @@ -2054,7 +2051,7 @@ namespace System.Threading.Tasks // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); - if(tasks.Length == 0) ThrowHelper.ThrowArgumentException( ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); + if (tasks.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); @@ -2076,8 +2073,8 @@ namespace System.Threading.Tasks if (continuationFunction != null) { return starter.ContinueWith( - //the following delegate avoids closure capture as much as possible - //completedTask.Result is the winning task; state == continuationAction + //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); } @@ -2132,7 +2129,7 @@ namespace System.Threading.Tasks Debug.Assert(continuationAction != null); return starter.ContinueWith<TResult>( // Use a cached delegate - GenericDelegateCache<TAntecedentResult,TResult>.CWAnyActionDelegate, + GenericDelegateCache<TAntecedentResult, TResult>.CWAnyActionDelegate, continuationAction, scheduler, cancellationToken, continuationOptions); } } @@ -2180,7 +2177,5 @@ namespace System.Threading.Tasks action(wrappedAntecedents.Result); return default(TResult); }; - } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs index 32efd771a0..17dd1f8bde 100644 --- a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs +++ b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs @@ -13,6 +13,7 @@ using System.Runtime.InteropServices.WindowsRuntime; // Windows.Foundation.Diagnostics cannot be referenced from managed code because // they're hidden by the metadata adapter. We redeclare the interfaces manually // to be able to talk to native WinRT objects. + namespace Windows.Foundation.Diagnostics { [ComImport] @@ -47,16 +48,16 @@ namespace Windows.Foundation.Diagnostics [WindowsRuntimeImport] internal sealed class TracingStatusChangedEventArgs : ITracingStatusChangedEventArgs { - public extern bool Enabled + public extern bool Enabled { [MethodImpl(MethodImplOptions.InternalCall)] get; } - - public extern CausalityTraceLevel TraceLevel + + public extern CausalityTraceLevel TraceLevel { [MethodImpl(MethodImplOptions.InternalCall)] - get; + get; } } @@ -97,5 +98,4 @@ namespace Windows.Foundation.Diagnostics Error = 3, Started = 0 } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs index 545bf9a5e5..f9d5f89398 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs @@ -182,7 +182,8 @@ namespace System.Threading.Tasks newSegment.m_state.m_last = 1; newSegment.m_state.m_lastCopy = 1; - try { } finally + try { } + finally { // Finally block to protect against corruption due to a thread abort // between setting m_next and setting m_tail. @@ -271,8 +272,8 @@ namespace System.Threading.Tasks { for (Segment segment = m_head; segment != null; segment = segment.m_next) { - for (int pt = segment.m_state.m_first; - pt != segment.m_state.m_last; + for (int pt = segment.m_state.m_first; + pt != segment.m_state.m_last; pt = (pt + 1) & (segment.m_array.Length - 1)) { yield return segment.m_array[pt]; @@ -307,7 +308,7 @@ namespace System.Threading.Tasks } /// <summary>A segment in the queue containing one or more items.</summary> - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential)] private sealed class Segment { /// <summary>The next segment in the linked list of segments.</summary> @@ -367,7 +368,7 @@ namespace System.Threading.Tasks } /// <summary>A placeholder class for common padding constants and eventually routines.</summary> - static class PaddingHelpers + internal static class PaddingHelpers { /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary> internal const int CACHE_LINE_SIZE = 128; @@ -375,8 +376,7 @@ namespace System.Threading.Tasks /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary> [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines - struct PaddingFor32 + internal struct PaddingFor32 { } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs index 12cc1daa63..33bf792370 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs @@ -16,21 +16,21 @@ using System.Text; using System.Security; using System.Runtime.CompilerServices; +using System.Diagnostics.Tracing; + namespace System.Threading.Tasks { - using System.Diagnostics.Tracing; - /// <summary>Provides an event source for tracing TPL information.</summary> [EventSource( Name = "System.Threading.Tasks.TplEventSource", - Guid = "2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5", - LocalizationResources = System.CoreLib.Name)] + Guid = "2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5", + LocalizationResources = "FxResources.System.Private.CoreLib.SR")] internal sealed class TplEtwProvider : EventSource { /// Used to determine if tasks should generate Activity IDs for themselves internal bool TasksSetActivityIds; // This keyword is set internal bool Debug; - private bool DebugActivityId; + private bool DebugActivityId; /// <summary> /// Get callbacks when the ETW sends us commands` @@ -42,11 +42,11 @@ namespace System.Threading.Tasks AsyncCausalityTracer.EnableToETW(true); else if (command.Command == EventCommand.Disable) AsyncCausalityTracer.EnableToETW(false); - - if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds)) + + if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds)) ActivityTracker.Instance.Enable(); - else - TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds); + else + TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds); Debug = IsEnabled(EventLevel.Informational, Keywords.Debug); DebugActivityId = IsEnabled(EventLevel.Informational, Keywords.DebugActivityId); @@ -99,49 +99,49 @@ namespace System.Threading.Tasks /// This sets activity IDS and logs when tasks are schedules (or waits begin) /// But are otherwise silent /// </summary> - public const EventKeywords TaskTransfer = (EventKeywords) 1; + public const EventKeywords TaskTransfer = (EventKeywords)1; /// <summary> /// TaskTranser events plus events when tasks start and stop /// </summary> - public const EventKeywords Tasks = (EventKeywords) 2; + public const EventKeywords Tasks = (EventKeywords)2; /// <summary> /// Events associted with the higher level parallel APIs /// </summary> - public const EventKeywords Parallel = (EventKeywords) 4; + public const EventKeywords Parallel = (EventKeywords)4; /// <summary> /// These are relatively verbose events that effectively just redirect /// the windows AsyncCausalityTracer to ETW /// </summary> - public const EventKeywords AsyncCausalityOperation = (EventKeywords) 8; - public const EventKeywords AsyncCausalityRelation = (EventKeywords) 0x10; - public const EventKeywords AsyncCausalitySynchronousWork = (EventKeywords) 0x20; + public const EventKeywords AsyncCausalityOperation = (EventKeywords)8; + public const EventKeywords AsyncCausalityRelation = (EventKeywords)0x10; + public const EventKeywords AsyncCausalitySynchronousWork = (EventKeywords)0x20; /// <summary> /// Emit the stops as well as the schedule/start events /// </summary> - public const EventKeywords TaskStops = (EventKeywords) 0x40; + public const EventKeywords TaskStops = (EventKeywords)0x40; /// <summary> /// TasksFlowActivityIds indicate that activity ID flow from one task /// to any task created by it. /// </summary> - public const EventKeywords TasksFlowActivityIds = (EventKeywords) 0x80; + public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80; /// <summary> /// TasksSetActivityIds will cause the task operations to set Activity Ids /// This option is incompatible with TasksFlowActivityIds flow is ignored /// if that keyword is set. This option is likley to be removed in the future /// </summary> - public const EventKeywords TasksSetActivityIds = (EventKeywords) 0x10000; + public const EventKeywords TasksSetActivityIds = (EventKeywords)0x10000; /// <summary> /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future /// </summary> - public const EventKeywords Debug = (EventKeywords) 0x20000; + public const EventKeywords Debug = (EventKeywords)0x20000; /// <summary> /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future /// </summary> - public const EventKeywords DebugActivityId = (EventKeywords) 0x40000; + public const EventKeywords DebugActivityId = (EventKeywords)0x40000; } /// <summary>Enabled for all keywords.</summary> @@ -182,18 +182,18 @@ namespace System.Threading.Tasks /// <summary>A continuation of a taskWaitEnd is complete </summary> private const int TASKWAITCONTINUATIONSTARTED_ID = 19; - private const int TRACEOPERATIONSTART_ID = 14; - private const int TRACEOPERATIONSTOP_ID = 15; - private const int TRACEOPERATIONRELATION_ID = 16; + private const int TRACEOPERATIONSTART_ID = 14; + private const int TRACEOPERATIONSTOP_ID = 15; + private const int TRACEOPERATIONRELATION_ID = 16; private const int TRACESYNCHRONOUSWORKSTART_ID = 17; - private const int TRACESYNCHRONOUSWORKSTOP_ID = 18; + private const int TRACESYNCHRONOUSWORKSTOP_ID = 18; + - //----------------------------------------------------------------------------------- // // Task Events // - + // These are all verbose events, so we need to call IsEnabled(EventLevel.Verbose, ALL_KEYWORDS) // call. However since the IsEnabled(l,k) call is more expensive than IsEnabled(), we only want // to incur this cost when instrumentation is enabled. So the Task codepaths that call these @@ -208,36 +208,36 @@ namespace System.Threading.Tasks /// <param name="TaskID">The task ID.</param> /// <param name="CreatingTaskID">The task ID</param> /// <param name="TaskCreationOptions">The options used to create the task.</param> - [Event(TASKSCHEDULED_ID, Task = Tasks.TaskScheduled, Version=1, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(TASKSCHEDULED_ID, Task = Tasks.TaskScheduled, Version = 1, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void TaskScheduled( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, int CreatingTaskID, int TaskCreationOptions, int appDomain) { // IsEnabled() call is an inlined quick check that makes this very fast when provider is off - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) { unsafe { EventData* eventPayload = stackalloc EventData[6]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[2].DataPointer = ((IntPtr)(&TaskID)); eventPayload[3].Size = sizeof(int); - eventPayload[3].DataPointer = ((IntPtr) (&CreatingTaskID)); + eventPayload[3].DataPointer = ((IntPtr)(&CreatingTaskID)); eventPayload[4].Size = sizeof(int); - eventPayload[4].DataPointer = ((IntPtr) (&TaskCreationOptions)); + eventPayload[4].DataPointer = ((IntPtr)(&TaskCreationOptions)); eventPayload[5].Size = sizeof(int); - eventPayload[5].DataPointer = ((IntPtr) (&appDomain)); + eventPayload[5].DataPointer = ((IntPtr)(&appDomain)); if (TasksSetActivityIds) { Guid childActivityId = CreateGuidForTaskID(TaskID); WriteEventWithRelatedActivityIdCore(TASKSCHEDULED_ID, &childActivityId, 6, eventPayload); } - else + else WriteEventCore(TASKSCHEDULED_ID, 6, eventPayload); } } @@ -251,13 +251,13 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> - [Event(TASKSTARTED_ID, + [Event(TASKSTARTED_ID, Level = EventLevel.Informational, Keywords = Keywords.Tasks)] public void TaskStarted( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID) { - if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) + if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) WriteEvent(TASKSTARTED_ID, OriginatingTaskSchedulerID, OriginatingTaskID, TaskID); } #endregion @@ -270,33 +270,33 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> /// <param name="IsExceptional">Whether the task completed due to an error.</param> - [Event(TASKCOMPLETED_ID, Version=1, + [Event(TASKCOMPLETED_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.TaskStops)] public void TaskCompleted( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, bool IsExceptional) { - if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) + if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) { unsafe { EventData* eventPayload = stackalloc EventData[4]; Int32 isExceptionalInt = IsExceptional ? 1 : 0; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[2].DataPointer = ((IntPtr)(&TaskID)); eventPayload[3].Size = sizeof(int); - eventPayload[3].DataPointer = ((IntPtr) (&isExceptionalInt)); + eventPayload[3].DataPointer = ((IntPtr)(&isExceptionalInt)); WriteEventCore(TASKCOMPLETED_ID, 4, eventPayload); } - } + } } #endregion - #region TaskWaitBegin + #region TaskWaitBegin /// <summary> /// Fired when starting to wait for a taks's completion explicitly or implicitly. /// </summary> @@ -307,13 +307,13 @@ namespace System.Threading.Tasks /// <param name="ContinueWithTaskID">If known, if 'TaskID' has a 'continueWith' task, mention give its ID here. /// 0 means unknown. This allows better visualization of the common sequential chaining case.</param> /// </summary> - [Event(TASKWAITBEGIN_ID, Version=3, Task = TplEtwProvider.Tasks.TaskWait, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(TASKWAITBEGIN_ID, Version = 3, Task = TplEtwProvider.Tasks.TaskWait, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void TaskWaitBegin( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, TaskWaitBehavior Behavior, int ContinueWithTaskID) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) { unsafe { @@ -348,7 +348,7 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> - [Event(TASKWAITEND_ID, + [Event(TASKWAITEND_ID, Level = EventLevel.Verbose, Keywords = Keywords.Tasks)] public void TaskWaitEnd( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER @@ -395,23 +395,23 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The activityId for the continuation.</param> - [Event(AWAITTASKCONTINUATIONSCHEDULED_ID, Task = Tasks.AwaitTaskContinuationScheduled, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(AWAITTASKCONTINUATIONSCHEDULED_ID, Task = Tasks.AwaitTaskContinuationScheduled, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void AwaitTaskContinuationScheduled( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int ContinuwWithTaskId) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) - { + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) + { unsafe { EventData* eventPayload = stackalloc EventData[3]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&ContinuwWithTaskId)); + eventPayload[2].DataPointer = ((IntPtr)(&ContinuwWithTaskId)); if (TasksSetActivityIds) { Guid continuationActivityId = CreateGuidForTaskID(ContinuwWithTaskId); @@ -423,116 +423,116 @@ namespace System.Threading.Tasks } } - [Event(TRACEOPERATIONSTART_ID, Version=1, + [Event(TRACEOPERATIONSTART_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)] public void TraceOperationBegin(int TaskID, string OperationName, long RelatedContext) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) { unsafe { - fixed(char* operationNamePtr = OperationName) + fixed (char* operationNamePtr = OperationName) { EventData* eventPayload = stackalloc EventData[3]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[0].DataPointer = ((IntPtr)(&TaskID)); eventPayload[1].Size = ((OperationName.Length + 1) * 2); - eventPayload[1].DataPointer = ((IntPtr) operationNamePtr); + eventPayload[1].DataPointer = ((IntPtr)operationNamePtr); eventPayload[2].Size = sizeof(long); - eventPayload[2].DataPointer = ((IntPtr) (&RelatedContext)); + eventPayload[2].DataPointer = ((IntPtr)(&RelatedContext)); WriteEventCore(TRACEOPERATIONSTART_ID, 3, eventPayload); } } - } + } } - [Event(TRACEOPERATIONRELATION_ID, Version=1, + [Event(TRACEOPERATIONRELATION_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityRelation)] public void TraceOperationRelation(int TaskID, CausalityRelation Relation) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityRelation)) - WriteEvent(TRACEOPERATIONRELATION_ID, TaskID,(int) Relation); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityRelation)) + WriteEvent(TRACEOPERATIONRELATION_ID, TaskID, (int)Relation); // optmized overload for this exists } - [Event(TRACEOPERATIONSTOP_ID, Version=1, + [Event(TRACEOPERATIONSTOP_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)] public void TraceOperationEnd(int TaskID, AsyncCausalityStatus Status) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) - WriteEvent(TRACEOPERATIONSTOP_ID, TaskID,(int) Status); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) + WriteEvent(TRACEOPERATIONSTOP_ID, TaskID, (int)Status); // optmized overload for this exists } - [Event(TRACESYNCHRONOUSWORKSTART_ID, Version=1, + [Event(TRACESYNCHRONOUSWORKSTART_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)] public void TraceSynchronousWorkBegin(int TaskID, CausalitySynchronousWork Work) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) - WriteEvent(TRACESYNCHRONOUSWORKSTART_ID, TaskID,(int) Work); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) + WriteEvent(TRACESYNCHRONOUSWORKSTART_ID, TaskID, (int)Work); // optmized overload for this exists } - [Event(TRACESYNCHRONOUSWORKSTOP_ID, Version=1, + [Event(TRACESYNCHRONOUSWORKSTOP_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)] public void TraceSynchronousWorkEnd(CausalitySynchronousWork Work) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) { unsafe { EventData* eventPayload = stackalloc EventData[1]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&Work)); + eventPayload[0].DataPointer = ((IntPtr)(&Work)); WriteEventCore(TRACESYNCHRONOUSWORKSTOP_ID, 1, eventPayload); } - } + } } [NonEvent] - unsafe public void RunningContinuation(int TaskID, object Object) { RunningContinuation(TaskID, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); } + 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) - { + private void RunningContinuation(int TaskID, long Object) + { if (Debug) - WriteEvent(20, TaskID, Object); + WriteEvent(20, TaskID, Object); } [NonEvent] - unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); } + unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long)*((void**)JitHelpers.UnsafeCastToStackPointer(ref Object))); } [Event(21, Keywords = Keywords.Debug)] - public void RunningContinuationList(int TaskID, int Index, long Object) - { + public void RunningContinuationList(int TaskID, int Index, long Object) + { if (Debug) - WriteEvent(21, TaskID, Index, Object); - } + WriteEvent(21, TaskID, Index, Object); + } [Event(23, Keywords = Keywords.Debug)] - public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(23, Facility, Message); } + public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(23, Facility, Message); } [Event(24, Keywords = Keywords.Debug)] - public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); } + public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); } [Event(25, Keywords = Keywords.DebugActivityId)] public void SetActivityId(Guid NewId) { if (DebugActivityId) - WriteEvent(25, NewId); - } + WriteEvent(25, NewId); + } [Event(26, Keywords = Keywords.Debug)] - public void NewID(int TaskID) - { + public void NewID(int TaskID) + { if (Debug) - WriteEvent(26, TaskID); - } + WriteEvent(26, TaskID); + } /// <summary> /// Activity IDs are GUIDS but task IDS are integers (and are not unique across appdomains /// This routine creates a process wide unique GUID given a task ID /// </summary> - internal static Guid CreateGuidForTaskID(int taskID) + internal static Guid CreateGuidForTaskID(int taskID) { // The thread pool generated a process wide unique GUID from a task GUID by // using the taskGuid, the appdomain ID, and 8 bytes of 'randomization' chosen by @@ -540,9 +540,9 @@ namespace System.Threading.Tasks // These were generated by CreateGuid, and are reasonably random (and thus unlikley to collide uint pid = EventSource.s_currentPid; int appDomainID = System.Threading.Thread.GetDomainID(); - return new Guid(taskID, - (short) appDomainID , (short) (appDomainID >> 16), - (byte)pid, (byte)(pid >> 8), (byte)(pid >> 16), (byte)(pid >> 24), + return new Guid(taskID, + (short)appDomainID, (short)(appDomainID >> 16), + (byte)pid, (byte)(pid >> 8), (byte)(pid >> 16), (byte)(pid >> 24), 0xff, 0xdc, 0xd7, 0xb5); } } diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs index 7013c5c5e0..8e2e6a4cb0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/Task.cs +++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs @@ -29,7 +29,6 @@ using System.Diagnostics.Tracing; namespace System.Threading.Tasks { - /// <summary> /// Utility class for allocating structs as heap variables /// </summary> @@ -41,7 +40,6 @@ namespace System.Threading.Tasks { this.Value = value; } - } /// <summary> @@ -271,7 +269,7 @@ namespace System.Threading.Tasks // but haven't yet been waited on by the parent, lazily initialized. internal volatile List<Task> m_exceptionalChildren; // A task's parent, or null if parent-less. Only set during Task construction. - internal Task m_parent; + internal Task m_parent; /// <summary> /// Sets the internal completion event. @@ -569,8 +567,8 @@ namespace System.Threading.Tasks #if DEBUG // Check the validity of internalOptions - int illegalInternalOptions = - (int) (internalOptions & + int illegalInternalOptions = + (int)(internalOptions & ~(InternalTaskOptions.PromiseTask | InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation | @@ -581,27 +579,26 @@ namespace System.Threading.Tasks // 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"); - var tmpFlags = (int)creationOptions | (int)internalOptions; - if ((m_action == null) || ((internalOptions & InternalTaskOptions.ContinuationTask) != 0)) - { - // For continuation tasks or TaskCompletionSource.Tasks, begin life in the - // WaitingForActivation state rather than the Created state. - tmpFlags |= TASK_STATE_WAITINGFORACTIVATION; - } - m_stateFlags = tmpFlags; // one write to the volatile m_stateFlags instead of two when setting the above options + int tmpFlags = (int)creationOptions | (int)internalOptions; // one write to the volatile m_stateFlags instead of two when setting the above options + m_stateFlags = m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != 0 ? + tmpFlags | TASK_STATE_WAITINGFORACTIVATION : + tmpFlags; // Now is the time to add the new task to the children list // of the creating task if the options call for it. // We can safely call the creator task's AddNewChild() method to register it, // because at this point we are already on its thread of execution. - Task parent = m_contingentProperties?.m_parent; - if (parent != null - && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) - && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) - ) + ContingentProperties props = m_contingentProperties; + if (props != null) { - parent.AddNewChild(); + Task parent = props.m_parent; + if (parent != null + && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) + && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)) + { + parent.AddNewChild(); + } } // if we have a non-null cancellationToken, allocate the contingent properties to save it @@ -716,14 +713,7 @@ namespace System.Threading.Tasks } // Internal property to process TaskCreationOptions access and mutation. - internal TaskCreationOptions Options - { - get - { - int stateFlags = m_stateFlags; // "cast away" volatility to enable inlining of OptionsMethod - return OptionsMethod(stateFlags); - } - } + internal TaskCreationOptions Options => OptionsMethod(m_stateFlags); // Similar to Options property, but allows for the use of a cached flags value rather than // a read of the volatile m_stateFlags field. @@ -737,11 +727,16 @@ namespace System.Threading.Tasks // no illegalBits are set. Returns true on success, false on failure. internal bool AtomicStateUpdate(int newBits, int illegalBits) { - // This could be implemented in terms of: - // internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags); - // but for high-throughput perf, that delegation's cost is noticeable. + int oldFlags = m_stateFlags; + return + (oldFlags & illegalBits) == 0 && + (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags || + AtomicStateUpdateSlow(newBits, illegalBits)); + } - SpinWait sw = new SpinWait(); + private bool AtomicStateUpdateSlow(int newBits, int illegalBits) + { + var sw = new SpinWait(); do { int oldFlags = m_stateFlags; @@ -900,7 +895,6 @@ namespace System.Threading.Tasks return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool FireTaskScheduledIfNeeded(TaskScheduler ts) { var etwLog = TplEtwProvider.Log; @@ -1789,7 +1783,7 @@ namespace System.Threading.Tasks catch (ThreadAbortException tae) { AddException(tae); - FinishThreadAbortedTask(delegateRan:false); + FinishThreadAbortedTask(delegateRan: false); } catch (Exception e) { @@ -2041,32 +2035,45 @@ namespace System.Threading.Tasks /// <summary> /// Signals completion of this particular task. /// - /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the + /// The userDelegateExecute parameter indicates whether this Finish() call comes following the /// full execution of the user delegate. /// - /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to + /// If userDelegateExecute is false, it mean user delegate wasn't invoked at all (either due to /// a cancellation request, or because this task is a promise style Task). In this case, the steps /// involving child tasks (i.e. WaitForChildren) will be skipped. /// /// </summary> - internal void Finish(bool bUserDelegateExecuted) + internal void Finish(bool userDelegateExecute) + { + if (m_contingentProperties == null) + { + FinishStageTwo(); + } + else + { + FinishSlow(userDelegateExecute); + } + } + + private void FinishSlow(bool userDelegateExecute) { - if (!bUserDelegateExecuted) + Debug.Assert(userDelegateExecute || m_contingentProperties != null); + + if (!userDelegateExecute) { // delegate didn't execute => no children. We can safely call the remaining finish stages FinishStageTwo(); } else { - var props = Volatile.Read(ref m_contingentProperties); + ContingentProperties props = m_contingentProperties; - if (props == null || // no contingent properties means no children, so it's safe to complete ourselves - (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. + // Count of 1 => either all children finished, or there were none. Safe to complete ourselves + // without paying the price of an Interlocked.Decrement. + if ((props.m_completionCountdown == 1) || 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. + // and we could be racing with one of them to call FinishStageTwo(). + // So whoever does the final Interlocked.Dec is responsible to finish. { FinishStageTwo(); } @@ -2086,8 +2093,7 @@ namespace System.Threading.Tasks // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw. // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren - List<Task> exceptionalChildren = props != null ? props.m_exceptionalChildren : null; - + List<Task> exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { lock (exceptionalChildren) @@ -2106,12 +2112,17 @@ namespace System.Threading.Tasks /// It can happen i) either on the thread that originally executed this task (if no children were spawned, or they all completed by the time this task's delegate quit) /// ii) or on the thread that executed the last child. /// </summary> - internal void FinishStageTwo() + private void FinishStageTwo() { - AddExceptionsFromChildren(); - // At this point, the task is done executing and waiting for its children, // we can transition our task to a completion state. + + ContingentProperties cp = Volatile.Read(ref m_contingentProperties); + if (cp != null) + { + AddExceptionsFromChildren(cp); + } + int completionState; if (ExceptionRecorded) { @@ -2160,7 +2171,7 @@ namespace System.Threading.Tasks // Set the completion event if it's been lazy allocated. // And if we made a cancellation registration, it's now unnecessary. - var cp = Volatile.Read(ref m_contingentProperties); + cp = Volatile.Read(ref m_contingentProperties); // need to re-read after updating state if (cp != null) { cp.SetCompleted(); @@ -2187,6 +2198,17 @@ namespace System.Threading.Tasks m_action = null; // Notify parent if this was an attached task + if (m_contingentProperties != null) + { + NotifyParentIfPotentiallyAttachedTask(); + } + + // Activate continuations (if any). + FinishContinuations(); + } + + internal void NotifyParentIfPotentiallyAttachedTask() + { Task parent = m_contingentProperties?.m_parent; if (parent != null && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) @@ -2194,9 +2216,6 @@ namespace System.Threading.Tasks { parent.ProcessChildCompletion(this); } - - // Activate continuations (if any). - FinishContinuations(); } /// <summary> @@ -2232,7 +2251,6 @@ namespace System.Threading.Tasks tmp.Add(childTask); } } - } if (Interlocked.Decrement(ref props.m_completionCountdown) == 0) @@ -2248,14 +2266,15 @@ namespace System.Threading.Tasks /// This is to be called just before the task does its final state transition. /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list /// </summary> - internal void AddExceptionsFromChildren() + internal void AddExceptionsFromChildren(ContingentProperties props) { + Debug.Assert(props != null); + // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren // being nulled out while it is being processed, which could lead to a NullReferenceException. To // protect ourselves, we'll cache m_exceptionalChildren in a local variable. - var props = Volatile.Read(ref m_contingentProperties); - List<Task> exceptionalChildren = props?.m_exceptionalChildren; + List<Task> exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { @@ -2310,40 +2329,13 @@ namespace System.Threading.Tasks Finish(delegateRan); } - - /// <summary> - /// Executes the task. This method will only be called once, and handles any necessary exception marshaling. - /// </summary> - private void Execute() - { - try - { - InnerInvoke(); - } - catch (ThreadAbortException 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(delegateRan: true); - } - catch (Exception exn) - { - // Record this exception in the task's exception list - HandleException(exn); - } - } - /// <summary> /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it. /// /// </summary> void IThreadPoolWorkItem.ExecuteWorkItem() { - ExecuteEntry(false); + ExecuteEntryUnsafe(); } /// <summary> @@ -2357,46 +2349,58 @@ namespace System.Threading.Tasks if (!IsCompleted) { HandleException(tae); - FinishThreadAbortedTask(delegateRan:false); + FinishThreadAbortedTask(delegateRan: false); } } /// <summary> /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread. - /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline. - /// /// </summary> - /// <param name="bPreventDoubleExecution"> Performs atomic updates to prevent double execution. Should only be set to true - /// in codepaths servicing user provided TaskSchedulers. The ThreadPool scheduler doesn't need this. </param> - internal bool ExecuteEntry(bool bPreventDoubleExecution) + internal bool ExecuteEntry() { - if (bPreventDoubleExecution) + // Do atomic state transition from queued to invoked. If we observe a task that's already invoked, + // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler. + // However we don't want this exception to be throw if the task was already canceled, because it's a + // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) + int previousState = 0; + if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, + TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, + ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) { - int previousState = 0; + // This task has already been invoked. Don't invoke it again. + return false; + } - // Do atomic state transition from queued to invoked. If we observe a task that's already invoked, - // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler. - // However we don't want this exception to be throw if the task was already canceled, because it's a - // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) - if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, - TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, - ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) - { - // This task has already been invoked. Don't invoke it again. - return false; - } + if (!IsCancellationRequested & !IsCanceled) + { + ExecuteWithThreadLocal(ref t_currentTask); } else { - // Remember that we started running the task delegate. - m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + ExecuteEntryCancellationRequestedOrCanceled(); } - if (!IsCancellationRequested && !IsCanceled) + return true; + } + + internal void ExecuteEntryUnsafe() // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent + { + // Remember that we started running the task delegate. + m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + + if (!IsCancellationRequested & !IsCanceled) { ExecuteWithThreadLocal(ref t_currentTask); } - else if (!IsCanceled) + else + { + ExecuteEntryCancellationRequestedOrCanceled(); + } + } + + internal void ExecuteEntryCancellationRequestedOrCanceled() + { + if (!IsCanceled) { int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); if ((prevState & TASK_STATE_CANCELED) == 0) @@ -2404,8 +2408,6 @@ namespace System.Threading.Tasks CancellationCleanupLogic(); } } - - return true; } // A trick so we can refer to the TLS slot with a byref. @@ -2429,30 +2431,44 @@ namespace System.Threading.Tasks etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id); } - if (AsyncCausalityTracer.LoggingOn) + bool loggingOn = AsyncCausalityTracer.LoggingOn; + if (loggingOn) AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution); - try { // place the current task into TLS. currentTaskSlot = this; - ExecutionContext ec = CapturedContext; - if (ec == null) + // Execute the task body + try { - // No context, just run the task directly. - Execute(); + ExecutionContext ec = CapturedContext; + if (ec == null) + { + // No context, just run the task directly. + InnerInvoke(); + } + else + { + // Invoke it under the captured ExecutionContext + ExecutionContext.Run(ec, s_ecCallback, this); + } } - else + catch (Exception exn) { - // Run the task. We need a simple shim that converts the - // object back into a Task object, so that we can Execute it. - - ExecutionContext.Run(ec, s_ecCallback, this); + // Record this exception in the task's exception list + HandleException(exn); + if (exn is ThreadAbortException) + { + // 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); + } } - if (AsyncCausalityTracer.LoggingOn) + if (loggingOn) AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution); Finish(true); @@ -2460,7 +2476,7 @@ namespace System.Threading.Tasks finally { currentTaskSlot = previousTask; - + // ETW event for Task Completed if (etwIsEnabled) { @@ -2476,7 +2492,7 @@ namespace System.Threading.Tasks } } - private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).Execute(); + private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).InnerInvoke(); /// <summary> /// The actual code which invokes the body of the task. This can be overriden in derived types. @@ -2973,7 +2989,6 @@ namespace System.Threading.Tasks { Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i)); } - } return IsCompleted; @@ -3031,12 +3046,10 @@ namespace System.Threading.Tasks // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc) mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED); - // PS: This is slightly different from the regular cancellation codepath // since we record the cancellation request *after* doing the state transition. // However that shouldn't matter too much because the task was never invoked, thus can't have children } - } if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup) @@ -3125,7 +3138,7 @@ namespace System.Threading.Tasks oce = edi.SourceException as OperationCanceledException; Debug.Assert(oce != null, "Expected EDI to contain an OCE"); } - Debug.Assert(oce.CancellationToken == tokenToRecord, + Debug.Assert(oce.CancellationToken == tokenToRecord, "Expected OCE's token to match the provided token."); #endif AddException(cancellationException, representsCancellation: true); @@ -3191,7 +3204,18 @@ namespace System.Threading.Tasks { // Atomically store the fact that this task is completing. From this point on, the adding of continuations will // result in the continuations being run/launched directly rather than being added to the continuation list. + // Then if we grabbed any continuations, run them. object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel); + if (continuationObject != null) + { + RunContinuations(continuationObject); + } + } + + private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined + { + Debug.Assert(continuationObject != null); + TplEtwProvider etw = TplEtwProvider.Log; bool tplEtwProviderLoggingEnabled = etw.IsEnabled(); if (tplEtwProviderLoggingEnabled) @@ -3199,136 +3223,125 @@ namespace System.Threading.Tasks etw.RunningContinuation(Id, continuationObject); } - // If continuationObject == null, then we don't have any continuations to process - if (continuationObject != null) - { - - if (AsyncCausalityTracer.LoggingOn) - AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification); + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification); - // Skip synchronous execution of continuations if this task's thread was aborted - bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) || - (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) || - ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0)); + // Skip synchronous execution of continuations if this task's thread was aborted + bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) || + (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) || + ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0)); - // Handle the single-Action case - Action singleAction = continuationObject as Action; - if (singleAction != null) - { - AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask); - LogFinishCompletionNotification(); - return; - } + // Handle the single-Action case + Action singleAction = continuationObject as Action; + if (singleAction != null) + { + AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask); + LogFinishCompletionNotification(); + return; + } - // Handle the single-ITaskCompletionAction case - ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; - if (singleTaskCompletionAction != null) + // Handle the single-ITaskCompletionAction case + ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; + if (singleTaskCompletionAction != null) + { + if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode) { - if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode) - { - singleTaskCompletionAction.Invoke(this); - } - else - { - ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false); - } - LogFinishCompletionNotification(); - return; + singleTaskCompletionAction.Invoke(this); } - - // Handle the single-TaskContinuation case - TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation; - if (singleTaskContinuation != null) + else { - singleTaskContinuation.Run(this, bCanInlineContinuations); - LogFinishCompletionNotification(); - return; + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false); } + LogFinishCompletionNotification(); + return; + } - // Not a single; attempt to cast as list - List<object> continuations = continuationObject as List<object>; + // Handle the single-TaskContinuation case + TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation; + if (singleTaskContinuation != null) + { + singleTaskContinuation.Run(this, bCanInlineContinuations); + LogFinishCompletionNotification(); + return; + } - if (continuations == null) - { - LogFinishCompletionNotification(); - return; // Not a single or a list; just return - } + // Not a single; it must be a list. + List<object> continuations = (List<object>)continuationObject; - // - // Begin processing of continuation list - // + // + // Begin processing of continuation list + // - // Wait for any concurrent adds or removes to be retired - lock (continuations) { } - int continuationCount = continuations.Count; + // Wait for any concurrent adds or removes to be retired + lock (continuations) { } + int continuationCount = continuations.Count; - // Fire the asynchronous continuations first ... - for (int i = 0; i < continuationCount; i++) + // Fire the asynchronous continuations first ... + for (int i = 0; i < continuationCount; i++) + { + // Synchronous continuation tasks will have the ExecuteSynchronously option, + // and we're looking for asynchronous tasks... + var tc = continuations[i] as StandardTaskContinuation; + if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0) { - // Synchronous continuation tasks will have the ExecuteSynchronously option, - // and we're looking for asynchronous tasks... - var tc = continuations[i] as StandardTaskContinuation; - if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0) + if (tplEtwProviderLoggingEnabled) { - if (tplEtwProviderLoggingEnabled) - { - etw.RunningContinuationList(Id, i, tc); - } - continuations[i] = null; // so that we can skip this later - tc.Run(this, bCanInlineContinuations); + etw.RunningContinuationList(Id, i, tc); } + continuations[i] = null; // so that we can skip this later + tc.Run(this, bCanInlineContinuations); } + } - // ... and then fire the synchronous continuations (if there are any). - // This includes ITaskCompletionAction, AwaitTaskContinuations, and - // Action delegates, which are all by default implicitly synchronous. - for (int i = 0; i < continuationCount; i++) + // ... and then fire the synchronous continuations (if there are any). + // This includes ITaskCompletionAction, AwaitTaskContinuations, and + // Action delegates, which are all by default implicitly synchronous. + for (int i = 0; i < continuationCount; i++) + { + object currentContinuation = continuations[i]; + if (currentContinuation == null) continue; + continuations[i] = null; // to enable free'ing up memory earlier + if (tplEtwProviderLoggingEnabled) { - object currentContinuation = continuations[i]; - if (currentContinuation == null) continue; - continuations[i] = null; // to enable free'ing up memory earlier - if (tplEtwProviderLoggingEnabled) - { - etw.RunningContinuationList(Id, i, currentContinuation); - } + etw.RunningContinuationList(Id, i, currentContinuation); + } - // If the continuation is an Action delegate, it came from an await continuation, - // and we should use AwaitTaskContinuation to run it. - Action ad = currentContinuation as Action; - if (ad != null) + // If the continuation is an Action delegate, it came from an await continuation, + // and we should use AwaitTaskContinuation to run it. + Action ad = currentContinuation as Action; + if (ad != null) + { + AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask); + } + else + { + // If it's a TaskContinuation object of some kind, invoke it. + TaskContinuation tc = currentContinuation as TaskContinuation; + if (tc != null) { - AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask); + // We know that this is a synchronous continuation because the + // asynchronous ones have been weeded out + tc.Run(this, bCanInlineContinuations); } + // Otherwise, it must be an ITaskCompletionAction, so invoke it. else { - // If it's a TaskContinuation object of some kind, invoke it. - TaskContinuation tc = currentContinuation as TaskContinuation; - if (tc != null) + Debug.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction"); + var action = (ITaskCompletionAction)currentContinuation; + + if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode) { - // We know that this is a synchronous continuation because the - // asynchronous ones have been weeded out - tc.Run(this, bCanInlineContinuations); + action.Invoke(this); } - // Otherwise, it must be an ITaskCompletionAction, so invoke it. else { - Debug.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction"); - var action = (ITaskCompletionAction)currentContinuation; - - if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode) - { - action.Invoke(this); - } - else - { - ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false); - } + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false); } } } - - LogFinishCompletionNotification(); } + + LogFinishCompletionNotification(); } private void LogFinishCompletionNotification() @@ -4140,29 +4153,29 @@ namespace System.Threading.Tasks out InternalTaskOptions internalOptions) { // This is used a couple of times below - TaskContinuationOptions NotOnAnything = + const TaskContinuationOptions NotOnAnything = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.NotOnRanToCompletion; - TaskContinuationOptions creationOptionsMask = + const TaskContinuationOptions CreationOptionsMask = TaskContinuationOptions.PreferFairness | TaskContinuationOptions.LongRunning | TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.HideScheduler | - TaskContinuationOptions.AttachedToParent| + TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.RunContinuationsAsynchronously; // Check that LongRunning and ExecuteSynchronously are not specified together - TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; - if ((continuationOptions & illegalMask) == illegalMask) + const TaskContinuationOptions IllegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; + if ((continuationOptions & IllegalMask) == IllegalMask) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_ESandLR); } // Check that no illegal options were specified if ((continuationOptions & - ~(creationOptionsMask | NotOnAnything | + ~(CreationOptionsMask | NotOnAnything | TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions); @@ -4175,14 +4188,12 @@ namespace System.Threading.Tasks } // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions - creationOptions = (TaskCreationOptions)(continuationOptions & creationOptionsMask); - - // internalOptions has at least ContinuationTask ... - internalOptions = InternalTaskOptions.ContinuationTask; + creationOptions = (TaskCreationOptions)(continuationOptions & CreationOptionsMask); - // ... and possibly LazyCancellation - if ((continuationOptions & TaskContinuationOptions.LazyCancellation) != 0) - internalOptions |= InternalTaskOptions.LazyCancellation; + // internalOptions has at least ContinuationTask and possibly LazyCancellation + internalOptions = (continuationOptions & TaskContinuationOptions.LazyCancellation) != 0 ? + InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation : + InternalTaskOptions.ContinuationTask; } @@ -4417,7 +4428,6 @@ namespace System.Threading.Tasks { // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up" continuationsLocalListRef[index] = null; - } } } @@ -4500,7 +4510,6 @@ namespace System.Threading.Tasks } return WaitAll(tasks, (int)totalMilliseconds); - } /// <summary> @@ -5020,6 +5029,10 @@ namespace System.Threading.Tasks signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result); Debug.Assert(signaledTaskIndex >= 0); } + else + { + TaskFactory.CommonCWAnyLogicCleanup(firstCompleted); + } } // We need to prevent the tasks array from being GC'ed until we come out of the wait. @@ -5103,7 +5116,7 @@ namespace System.Threading.Tasks Debug.Assert(succeeded, "This should always succeed on a new task."); return task; } - + /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary> /// <param name="cancellationToken">The token with which to complete the task.</param> /// <returns>The canceled task.</returns> @@ -5112,7 +5125,7 @@ namespace System.Threading.Tasks { return FromCanceled(cancellationToken); } - + /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary> /// <typeparam name="TResult">The type of the result returned by the task.</typeparam> /// <param name="cancellationToken">The token with which to complete the task.</param> @@ -5122,7 +5135,7 @@ namespace System.Threading.Tasks { return FromCanceled<TResult>(cancellationToken); } - + #endregion #region Run methods @@ -6120,8 +6133,8 @@ namespace System.Threading.Tasks internal virtual Delegate[] GetDelegateContinuationsForDebugger() { //Avoid an infinite loop by making sure the continuation object is not a reference to istelf. - if (this.m_continuationObject != this) - return GetDelegatesFromContinuationObject(this.m_continuationObject); + if (m_continuationObject != this) + return GetDelegatesFromContinuationObject(m_continuationObject); else return null; } @@ -6192,11 +6205,8 @@ namespace System.Threading.Tasks private static Task[] GetActiveTasks() { - return new List<Task>(s_currentActiveTasks.Values).ToArray(); } - - } internal sealed class CompletionActionInvoker : IThreadPoolWorkItem @@ -6715,5 +6725,4 @@ namespace System.Threading.Tasks public bool InvokeMayRunArbitraryCode { get { return true; } } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs deleted file mode 100644 index f15e3e783a..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs +++ /dev/null @@ -1,93 +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. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// An exception for task cancellations. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System.Threading.Tasks -{ - - /// <summary> - /// Represents an exception used to communicate task cancellation. - /// </summary> - [Serializable] - public class TaskCanceledException : OperationCanceledException - { - - [NonSerialized] - private Task m_canceledTask; // The task which has been canceled. - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class. - /// </summary> - public TaskCanceledException() : base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage")) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with a specified error message. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - public TaskCanceledException(string message) : base(message) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with a specified error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskCanceledException(string message, Exception innerException) : base(message, innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class - /// with a reference to the <see cref="T:System.Threading.Tasks.Task"/> that has been canceled. - /// </summary> - /// <param name="task">A task that has been canceled.</param> - public TaskCanceledException(Task task) : - base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage"), task!=null ? task.CancellationToken:new CancellationToken()) - { - m_canceledTask = task; - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with serialized data. - /// </summary> - /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param> - /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination. </param> - protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - - /// <summary> - /// Gets the task associated with this exception. - /// </summary> - /// <remarks> - /// It is permissible for no Task to be associated with a - /// <see cref="T:System.Threading.Tasks.TaskCanceledException"/>, in which case - /// this property will return null. - /// </remarks> - public Task Task - { - get { return m_canceledTask; } - } - - } - -} diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs index bf9f9cbb2c..0c80afd22c 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs @@ -131,7 +131,7 @@ namespace System.Threading.Tasks { // Spin wait until the completion is finalized by another thread. var sw = new SpinWait(); - while (!m_task.IsCompleted) + while (!m_task.IsCompleted) sw.SpinOnce(); } @@ -185,7 +185,7 @@ namespace System.Threading.Tasks public bool TrySetException(IEnumerable<Exception> exceptions) { if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); - + List<Exception> defensiveCopy = new List<Exception>(); foreach (Exception e in exceptions) { @@ -276,7 +276,7 @@ namespace System.Threading.Tasks public bool TrySetResult(TResult result) { bool rval = m_task.TrySetResult(result); - if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); + if (!rval) SpinUntilCompleted(); return rval; } @@ -346,7 +346,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> public void SetCanceled() { - if(!TrySetCanceled()) + if (!TrySetCanceled()) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs index 3c6ccd8dd4..848a0ecbc2 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs @@ -32,7 +32,7 @@ namespace System.Threading.Tasks 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>, + Contract.Requires(action is Action<Task> || action is Action<Task, object>, "Invalid delegate type in ContinuationTaskFromTask"); m_antecedent = antecedent; } @@ -45,7 +45,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationTaskFromTask."); m_antecedent = null; @@ -79,7 +79,7 @@ namespace System.Threading.Tasks 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>, + Contract.Requires(function is Func<Task, TResult> || function is Func<Task, object, TResult>, "Invalid delegate type in ContinuationResultTaskFromTask"); m_antecedent = antecedent; } @@ -92,7 +92,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationResultTaskFromTask."); m_antecedent = null; @@ -126,7 +126,7 @@ namespace System.Threading.Tasks 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>, + Contract.Requires(action is Action<Task<TAntecedentResult>> || action is Action<Task<TAntecedentResult>, object>, "Invalid delegate type in ContinuationTaskFromResultTask"); m_antecedent = antecedent; } @@ -139,7 +139,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationTaskFromResultTask."); m_antecedent = null; @@ -186,7 +186,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationResultTaskFromResultTask."); m_antecedent = null; @@ -276,7 +276,6 @@ namespace System.Threading.Tasks } internal abstract Delegate[] GetDelegateContinuationsForDebugger(); - } /// <summary>Provides the standard implementation of a task continuation.</summary> @@ -505,7 +504,8 @@ namespace System.Threading.Tasks // Create the continuation task task. If we're allowed to inline, try to do so. // The target scheduler may still deny us from executing on this thread, in which case this'll be queued. - var task = CreateTask(state => { + var task = CreateTask(state => + { try { ((Action)state)(); } catch (Exception exc) { ThrowAsyncIfNecessary(exc); } }, m_action, m_scheduler); @@ -558,10 +558,10 @@ namespace System.Threading.Tasks Contract.Requires(scheduler != null); return new Task( - action, state, null, default(CancellationToken), - TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler) - { - CapturedContext = m_capturedContext + action, state, null, default(CancellationToken), + TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler) + { + CapturedContext = m_capturedContext }; } @@ -590,7 +590,7 @@ namespace System.Threading.Tasks // We couldn't inline, so now we need to schedule it ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false); - } + } } /// <summary> @@ -626,7 +626,7 @@ namespace System.Threading.Tasks } /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary> - void ExecuteWorkItemHelper() + private void ExecuteWorkItemHelper() { var etwLog = TplEtwProvider.Log; Guid savedActivityId = Guid.Empty; @@ -645,7 +645,7 @@ namespace System.Threading.Tasks { m_action(); } - // If there is an execution context, get the cached delegate and run the action under the context. + // If there is an execution context, get the cached delegate and run the action under the context. else { ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action); @@ -815,5 +815,4 @@ namespace System.Threading.Tasks return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) }; } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs index ee1112a93f..1385d907e0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs @@ -70,9 +70,9 @@ namespace System.Threading.Tasks private static void EnsureADUnloadCallbackRegistered() { - if (s_adUnloadEventHandler == null && - Interlocked.CompareExchange( ref s_adUnloadEventHandler, - AppDomainUnloadCallback, + if (s_adUnloadEventHandler == null && + Interlocked.CompareExchange(ref s_adUnloadEventHandler, + AppDomainUnloadCallback, null) == null) { AppDomain.CurrentDomain.DomainUnload += s_adUnloadEventHandler; @@ -93,7 +93,7 @@ namespace System.Threading.Tasks // We need to do this filtering because all TaskExceptionHolders will be finalized during shutdown or unload // regardles of reachability of the task (i.e. even if the user code was about to observe the task's exception), // which can otherwise lead to spurious crashes during shutdown. - if (m_faultExceptions != null && !m_isHandled && + if (m_faultExceptions != null && !m_isHandled && !Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !s_domainUnloadStarted) { // We don't want to crash the finalizer thread if any ThreadAbortExceptions @@ -124,14 +124,14 @@ namespace System.Threading.Tasks // will have been marked as handled before even getting here. // Give users a chance to keep this exception from crashing the process - + // First, publish the unobserved exception and allow users to observe it AggregateException exceptionToThrow = new AggregateException( - Environment.GetResourceString("TaskExceptionHolder_UnhandledException"), + SR.TaskExceptionHolder_UnhandledException, m_faultExceptions); UnobservedTaskExceptionEventArgs ueea = new UnobservedTaskExceptionEventArgs(exceptionToThrow); TaskScheduler.PublishUnobservedTaskException(m_task, ueea); - + // Now, if we are still unobserved and we're configured to crash on unobserved, throw the exception. // We need to publish the event above even if we're not going to crash, hence // why this check doesn't come at the beginning of the method. @@ -164,7 +164,7 @@ namespace System.Threading.Tasks { Contract.Requires(exceptionObject != null, "TaskExceptionHolder.Add(): Expected a non-null exceptionObject"); Contract.Requires( - exceptionObject is Exception || exceptionObject is IEnumerable<Exception> || + exceptionObject is Exception || exceptionObject is IEnumerable<Exception> || exceptionObject is ExceptionDispatchInfo || exceptionObject is IEnumerable<ExceptionDispatchInfo>, "TaskExceptionHolder.Add(): Expected Exception, IEnumerable<Exception>, ExceptionDispatchInfo, or IEnumerable<ExceptionDispatchInfo>"); @@ -180,16 +180,16 @@ namespace System.Threading.Tasks private void SetCancellationException(object exceptionObject) { Contract.Requires(exceptionObject != null, "Expected exceptionObject to be non-null."); - - Debug.Assert(m_cancellationException == null, + + Debug.Assert(m_cancellationException == null, "Expected SetCancellationException to be called only once."); - // Breaking this assumption will overwrite a previously OCE, - // and implies something may be wrong elsewhere, since there should only ever be one. + // Breaking this assumption will overwrite a previously OCE, + // and implies something may be wrong elsewhere, since there should only ever be one. - Debug.Assert(m_faultExceptions == null, + Debug.Assert(m_faultExceptions == null, "Expected SetCancellationException to be called before any faults were added."); - // Breaking this assumption shouldn't hurt anything here, but it implies something may be wrong elsewhere. - // If this changes, make sure to only conditionally mark as handled below. + // Breaking this assumption shouldn't hurt anything here, but it implies something may be wrong elsewhere. + // If this changes, make sure to only conditionally mark as handled below. // Store the cancellation exception var oce = exceptionObject as OperationCanceledException; @@ -267,21 +267,21 @@ namespace System.Threading.Tasks exceptions.AddRange(ediColl); #if DEBUG Debug.Assert(exceptions.Count > 0, "There should be at least one dispatch info."); - foreach(var tmp in exceptions) + foreach (var tmp in exceptions) { Debug.Assert(tmp != null, "No dispatch infos should be null"); } #endif } - // Anything else is a programming error + // Anything else is a programming error else { - throw new ArgumentException(Environment.GetResourceString("TaskExceptionHolder_UnknownExceptionType"), nameof(exceptionObject)); + throw new ArgumentException(SR.TaskExceptionHolder_UnknownExceptionType, nameof(exceptionObject)); } } } } - + // If all of the exceptions are ThreadAbortExceptions and/or // AppDomainUnloadExceptions, we do not want the finalization diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs index 89ba2988ca..e193d0e4e2 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs @@ -40,30 +40,20 @@ namespace System.Threading.Tasks public class TaskFactory { // member variables - private CancellationToken m_defaultCancellationToken; - private TaskScheduler m_defaultScheduler; - private TaskCreationOptions m_defaultCreationOptions; - private TaskContinuationOptions m_defaultContinuationOptions; + private readonly CancellationToken m_defaultCancellationToken; + private readonly TaskScheduler m_defaultScheduler; + private readonly TaskCreationOptions m_defaultCreationOptions; + private readonly TaskContinuationOptions m_defaultContinuationOptions; - - private TaskScheduler DefaultScheduler - { - get - { - if (m_defaultScheduler == null) return TaskScheduler.Current; - else return m_defaultScheduler; - } - } + private TaskScheduler DefaultScheduler => m_defaultScheduler ?? TaskScheduler.Current; // sister method to above property -- avoids a TLS lookup private TaskScheduler GetDefaultScheduler(Task currTask) { - if (m_defaultScheduler != null) return m_defaultScheduler; - else if ((currTask != null) - && ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0) - ) - return currTask.ExecutingTaskScheduler; - else return TaskScheduler.Default; + return + m_defaultScheduler ?? + (currTask != null && (currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0 ? currTask.ExecutingTaskScheduler : + TaskScheduler.Default); } /* Constructors */ @@ -1528,9 +1518,9 @@ namespace System.Threading.Tasks { // Options detected here cause exceptions in FromAsync methods that take beginMethod as a parameter if ((creationOptions & TaskCreationOptions.LongRunning) != 0) - throw new ArgumentOutOfRangeException(nameof(creationOptions), Environment.GetResourceString("Task_FromAsync_LongRunning")); + throw new ArgumentOutOfRangeException(nameof(creationOptions), SR.Task_FromAsync_LongRunning); if ((creationOptions & TaskCreationOptions.PreferFairness) != 0) - throw new ArgumentOutOfRangeException(nameof(creationOptions), Environment.GetResourceString("Task_FromAsync_PreferFairness")); + throw new ArgumentOutOfRangeException(nameof(creationOptions), SR.Task_FromAsync_PreferFairness); } // Check for general validity of options @@ -2325,7 +2315,7 @@ namespace System.Threading.Tasks { Contract.Requires(tasks != null, "Expected non-null collection of tasks"); _tasks = tasks; - + if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAny", 0); @@ -2337,7 +2327,8 @@ namespace System.Threading.Tasks public void Invoke(Task completingTask) { - if (Interlocked.CompareExchange(ref m_firstTaskAlreadyCompleted, 1, 0) == 0) + if (m_firstTaskAlreadyCompleted == 0 && + Interlocked.Exchange(ref m_firstTaskAlreadyCompleted, 1) == 0) { if (AsyncCausalityTracer.LoggingOn) { @@ -2367,7 +2358,6 @@ namespace System.Threading.Tasks !task.IsCompleted) task.RemoveContinuation(this); } _tasks = null; - } } @@ -2382,17 +2372,18 @@ namespace System.Threading.Tasks { Contract.Requires(tasks != null); - // Create a promise task to be returned to the user + // Create a promise task to be returned to the user. + // (If this logic ever changes, also update CommonCWAnyLogicCleanup.) var promise = new CompleteOnInvokePromise(tasks); // At the completion of any of the tasks, complete the promise. bool checkArgsOnly = false; int numTasks = tasks.Count; - for(int i=0; i<numTasks; i++) + for (int i = 0; i < numTasks; i++) { var task = tasks[i]; - if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + if (task == null) throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); if (checkArgsOnly) continue; @@ -2430,6 +2421,17 @@ namespace System.Threading.Tasks return promise; } + /// <summary> + /// Cleans up the operations performed by CommonCWAnyLogic in a case where + /// the created continuation task is being discarded. + /// </summary> + /// <param name="continuation">The task returned from CommonCWAnyLogic.</param> + internal static void CommonCWAnyLogicCleanup(Task<Task> continuation) + { + // Force cleanup of the promise (e.g. removing continuations from each + // constituent task), by completing the promise with any value. + ((CompleteOnInvokePromise)continuation).Invoke(null); + } /// <summary> /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see> @@ -2663,7 +2665,7 @@ namespace System.Threading.Tasks if (continuationFunction == null) throw new ArgumentNullException(nameof(continuationFunction)); Contract.EndContractBlock(); - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null,continuationOptions, m_defaultCancellationToken, DefaultScheduler); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2775,7 +2777,7 @@ namespace System.Threading.Tasks if (continuationFunction == null) throw new ArgumentNullException(nameof(continuationFunction)); Contract.EndContractBlock(); - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -3018,7 +3020,7 @@ namespace System.Threading.Tasks if (tasks == null) throw new ArgumentNullException(nameof(tasks)); if (tasks.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_EmptyTaskList, nameof(tasks)); Contract.EndContractBlock(); Task[] tasksCopy = new Task[tasks.Length]; @@ -3027,7 +3029,7 @@ namespace System.Threading.Tasks tasksCopy[i] = tasks[i]; if (tasksCopy[i] == null) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); } return tasksCopy; @@ -3038,7 +3040,7 @@ namespace System.Threading.Tasks if (tasks == null) throw new ArgumentNullException(nameof(tasks)); if (tasks.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_EmptyTaskList, nameof(tasks)); Contract.EndContractBlock(); Task<TResult>[] tasksCopy = new Task<TResult>[tasks.Length]; @@ -3047,7 +3049,7 @@ namespace System.Threading.Tasks tasksCopy[i] = tasks[i]; if (tasksCopy[i] == null) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); } return tasksCopy; @@ -3066,7 +3068,7 @@ namespace System.Threading.Tasks const TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; if ((continuationOptions & illegalMask) == illegalMask) { - throw new ArgumentOutOfRangeException(nameof(continuationOptions), Environment.GetResourceString("Task_ContinueWith_ESandLR")); + throw new ArgumentOutOfRangeException(nameof(continuationOptions), SR.Task_ContinueWith_ESandLR); } // Check that no nonsensical options are specified. @@ -3085,9 +3087,8 @@ namespace System.Threading.Tasks // Check that no "fire" options are specified. if ((continuationOptions & NotOnAny) != 0) - throw new ArgumentOutOfRangeException(nameof(continuationOptions), Environment.GetResourceString("Task_MultiTaskContinuation_FireOptions")); + throw new ArgumentOutOfRangeException(nameof(continuationOptions), SR.Task_MultiTaskContinuation_FireOptions); Contract.EndContractBlock(); } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs index d68c3fedc4..45d398f0eb 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs @@ -11,6 +11,7 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Disable the "reference to volatile field not treated as volatile" error. #pragma warning disable 0420 + using System; using System.Collections.Generic; using System.Globalization; @@ -23,7 +24,6 @@ using System.Runtime.CompilerServices; namespace System.Threading.Tasks { - /// <summary> /// Represents an abstract scheduler for tasks. /// </summary> @@ -46,7 +46,7 @@ namespace System.Threading.Tasks // // User Provided Methods and Properties // - + /// <summary> /// Queues a <see cref="T:System.Threading.Tasks.Task">Task</see> to the scheduler. /// </summary> @@ -168,7 +168,7 @@ namespace System.Threading.Tasks // // Internal overridable methods // - + /// <summary> /// Attempts to execute the target task synchronously. @@ -183,14 +183,14 @@ namespace System.Threading.Tasks // Do not inline TaskCompletionSource-style (a.k.a. "promise") tasks. // No need to attempt inlining if the task body was already run (i.e. either TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bits set) TaskScheduler ets = task.ExecutingTaskScheduler; - + // Delegate cross-scheduler inlining requests to target scheduler - if(ets != this && ets !=null) return ets.TryRunInline(task, taskWasPreviouslyQueued); + if (ets != this && ets != null) return ets.TryRunInline(task, taskWasPreviouslyQueued); StackGuard currentStackGuard; - if( (ets == null) || + if ((ets == null) || (task.m_action == null) || - task.IsDelegateInvoked || + task.IsDelegateInvoked || task.IsCanceled || (currentStackGuard = Task.CurrentStackGuard).TryBeginInliningScope() == false) { @@ -203,7 +203,10 @@ namespace System.Threading.Tasks bool bInlined = false; try { - task.FireTaskScheduledIfNeeded(this); + if (TplEtwProvider.Log.IsEnabled()) + { + task.FireTaskScheduledIfNeeded(this); + } bInlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued); } finally @@ -213,9 +216,9 @@ namespace System.Threading.Tasks // If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set // Otherwise the scheduler is buggy - if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled)) + if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled)) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_InconsistentStateAfterTryExecuteTaskInline")); + throw new InvalidOperationException(SR.TaskScheduler_InconsistentStateAfterTryExecuteTaskInline); } return bInlined; @@ -237,7 +240,7 @@ namespace System.Threading.Tasks /// Notifies the scheduler that a work item has made progress. /// </summary> internal virtual void NotifyWorkItemProgress() - { + { } /// <summary> @@ -256,7 +259,10 @@ namespace System.Threading.Tasks { Contract.Requires(task != null); - task.FireTaskScheduledIfNeeded(this); + if (TplEtwProvider.Log.IsEnabled()) + { + task.FireTaskScheduledIfNeeded(this); + } this.QueueTask(task); } @@ -269,7 +275,7 @@ namespace System.Threading.Tasks // The global container that keeps track of TaskScheduler instances for debugging purposes. private static ConditionalWeakTable<TaskScheduler, object> s_activeTaskSchedulers; - + // An AppDomain-wide default manager. private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler(); @@ -316,7 +322,7 @@ namespace System.Threading.Tasks /// <summary> /// Gets the default <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> instance. /// </summary> - public static TaskScheduler Default + public static TaskScheduler Default { get { @@ -331,7 +337,7 @@ namespace System.Threading.Tasks /// <remarks> /// When not called from within a task, <see cref="Current"/> will return the <see cref="Default"/> scheduler. /// </remarks> - public static TaskScheduler Current + public static TaskScheduler Current { get { @@ -352,7 +358,7 @@ namespace System.Threading.Tasks get { Task currentTask = Task.InternalCurrent; - return ( (currentTask != null) + return ((currentTask != null) && ((currentTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0) ) ? currentTask.ExecutingTaskScheduler : null; } @@ -398,7 +404,7 @@ namespace System.Threading.Tasks { newId = Interlocked.Increment(ref s_taskSchedulerIdCounter); } while (newId == 0); - + Interlocked.CompareExchange(ref m_taskSchedulerId, newId, 0); } @@ -437,10 +443,10 @@ namespace System.Threading.Tasks { if (task.ExecutingTaskScheduler != this) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_ExecuteTask_WrongTaskScheduler")); + throw new InvalidOperationException(SR.TaskScheduler_ExecuteTask_WrongTaskScheduler); } - return task.ExecuteEntry(true); + return task.ExecuteEntry(); } //////////////////////////////////////////////////////////// @@ -477,12 +483,12 @@ namespace System.Threading.Tasks lock (_unobservedTaskExceptionLockObject) _unobservedTaskException -= value; } } - - + + //////////////////////////////////////////////////////////// // // Internal methods @@ -588,19 +594,18 @@ namespace System.Threading.Tasks m_taskScheduler = scheduler; } - // returns the scheduler’s Id + // returns the scheduler�s Id public Int32 Id - { - get { return m_taskScheduler.Id; } + { + get { return m_taskScheduler.Id; } } - // returns the scheduler’s GetScheduledTasks - public IEnumerable<Task> ScheduledTasks + // returns the scheduler�s GetScheduledTasks + public IEnumerable<Task> ScheduledTasks { get { return m_taskScheduler.GetScheduledTasks(); } } } - } @@ -626,11 +631,10 @@ namespace System.Threading.Tasks // make sure we have a synccontext to work with if (synContext == null) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_FromCurrentSynchronizationContext_NoCurrent")); + throw new InvalidOperationException(SR.TaskScheduler_FromCurrentSynchronizationContext_NoCurrent); } m_synchronizationContext = synContext; - } /// <summary> @@ -684,16 +688,7 @@ namespace System.Threading.Tasks } // preallocated SendOrPostCallback delegate - private static SendOrPostCallback s_postCallback = new SendOrPostCallback(PostCallback); - - // this is where the actual task invocation occures - private static void PostCallback(object obj) - { - Task task = (Task) obj; - - // calling ExecuteEntry with double execute check enabled because a user implemented SynchronizationContext could be buggy - task.ExecuteEntry(true); - } + private static readonly SendOrPostCallback s_postCallback = s => ((Task)s).ExecuteEntry(); // with double-execute check because SC could be buggy } /// <summary> @@ -728,7 +723,7 @@ namespace System.Threading.Tasks /// Gets whether this exception has been marked as "observed." /// </summary> public bool Observed { get { return m_observed; } } - + /// <summary> /// The Exception that went unobserved. /// </summary> diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs deleted file mode 100644 index 1d85e49342..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs +++ /dev/null @@ -1,81 +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. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// An exception for task schedulers. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System.Threading.Tasks -{ - - /// <summary> - /// Represents an exception used to communicate an invalid operation by a - /// <see cref="T:System.Threading.Tasks.TaskScheduler"/>. - /// </summary> - [Serializable] - public class TaskSchedulerException : Exception - { - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> class. - /// </summary> - public TaskSchedulerException() : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage")) // - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with a specified error message. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - public TaskSchedulerException(string message) : base(message) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class using the default error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskSchedulerException(Exception innerException) - : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage"), innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with a specified error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskSchedulerException(string message, Exception innerException) : base(message, innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with serialized data. - /// </summary> - /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds - /// the serialized object data about the exception being thrown.</param> - /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that - /// contains contextual information about the source or destination. </param> - protected TaskSchedulerException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - - } - -} diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs index 90743aeec5..fdd62c95f5 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs @@ -60,7 +60,7 @@ namespace System.Threading.Tasks { // Asynchronous completion asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false); - if (callback != null) + if (callback != null) InvokeCallbackWhenTaskCompletes(task, callback, asyncResult); } return asyncResult; @@ -129,7 +129,7 @@ namespace System.Threading.Tasks // We use OnCompleted rather than ContinueWith in order to avoid running synchronously // if the task has already completed by the time we get here. This is separated out into // its own method currently so that we only pay for the closure if necessary. - antecedent.ConfigureAwait(continueOnCapturedContext:false) + antecedent.ConfigureAwait(continueOnCapturedContext: false) .GetAwaiter() .OnCompleted(() => callback(asyncResult)); @@ -167,7 +167,7 @@ namespace System.Threading.Tasks /// <param name="task">The Task to wrap.</param> /// <param name="state">The new AsyncState value</param> /// <param name="completedSynchronously">The new CompletedSynchronously value.</param> - internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) + internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) { Contract.Requires(task != null); Contract.Requires(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed."); diff --git a/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs index 5c6ca9bb76..e69a89fe66 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs @@ -23,7 +23,7 @@ namespace System.Threading.Tasks /// <summary> /// An implementation of TaskScheduler that uses the ThreadPool scheduler /// </summary> - internal sealed class ThreadPoolTaskScheduler: TaskScheduler + internal sealed class ThreadPoolTaskScheduler : TaskScheduler { /// <summary> /// Constructs a new ThreadPool task scheduler object @@ -34,15 +34,7 @@ namespace System.Threading.Tasks } // static delegate for threads allocated to handle LongRunning tasks. - private static readonly ParameterizedThreadStart s_longRunningThreadWork = new ParameterizedThreadStart(LongRunningThreadWork); - - private static void LongRunningThreadWork(object obj) - { - Contract.Requires(obj != null, "TaskScheduler.LongRunningThreadWork: obj is null"); - Task t = obj as Task; - Debug.Assert(t != null, "TaskScheduler.LongRunningThreadWork: t is null"); - t.ExecuteEntry(false); - } + private static readonly ParameterizedThreadStart s_longRunningThreadWork = s => ((Task)s).ExecuteEntryUnsafe(); /// <summary> /// Schedules a task to the ThreadPool. @@ -64,11 +56,11 @@ namespace System.Threading.Tasks ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue); } } - + /// <summary> /// This internal function will do this: /// (1) If the task had previously been queued, attempt to pop it and return false if that fails. - /// (2) Propagate the return value from Task.ExecuteEntry() back to the caller. + /// (2) Return whether the task is executed /// /// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs /// to account for exceptions that need to be propagated, and throw themselves accordingly. @@ -79,19 +71,17 @@ namespace System.Threading.Tasks if (taskWasPreviouslyQueued && !ThreadPool.TryPopCustomWorkItem(task)) return false; - // Propagate the return value of Task.ExecuteEntry() - bool rval = false; try { - rval = task.ExecuteEntry(false); // handles switching Task.Current etc. + task.ExecuteEntryUnsafe(); // handles switching Task.Current etc. } finally { // Only call NWIP() if task was previously queued - if(taskWasPreviouslyQueued) NotifyWorkItemProgress(); + if (taskWasPreviouslyQueued) NotifyWorkItemProgress(); } - return rval; + return true; } protected internal override bool TryDequeue(Task task) diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs index 15136f12bf..26c2388e6b 100644 --- a/src/mscorlib/src/System/Threading/Tasks/future.cs +++ b/src/mscorlib/src/System/Threading/Tasks/future.cs @@ -82,20 +82,20 @@ namespace System.Threading.Tasks internal static readonly Func<Task<Task>, Task<TResult>> TaskWhenAnyCast = completed => (Task<TResult>)completed.Result; // Construct a promise-style task without any options. - internal Task() : + internal Task() : base() { } // Construct a promise-style task with state and options. internal Task(object state, TaskCreationOptions options) : - base(state, options, promiseStyle:true) + base(state, options, promiseStyle: true) { } // Construct a pre-completed Task<TResult> - internal Task(TResult result) : + internal Task(TResult result) : base(false, TaskCreationOptions.None, default(CancellationToken)) { m_result = result; @@ -372,9 +372,9 @@ namespace System.Threading.Tasks // Debugger support private string DebuggerDisplayResultDescription { - get + get { - return IsRanToCompletion ? "" + m_result : Environment.GetResourceString("TaskT_DebuggerNoResult"); + return IsRanToCompletion ? "" + m_result : SR.TaskT_DebuggerNoResult; } } @@ -392,7 +392,6 @@ namespace System.Threading.Tasks // internal helper function breaks out logic used by TaskCompletionSource internal bool TrySetResult(TResult result) { - if (IsCompleted) return false; Debug.Assert(m_action == null, "Task<T>.TrySetResult(): non-null m_action"); // "Reserve" the completion for this task, while making sure that: (1) No prior reservation @@ -413,12 +412,13 @@ namespace System.Threading.Tasks // and which can be summarized more concisely with the following snippet from // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult. Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); - - var cp = m_contingentProperties; - if (cp != null) cp.SetCompleted(); - - FinishStageThree(); - + ContingentProperties props = m_contingentProperties; + if (props != null) + { + NotifyParentIfPotentiallyAttachedTask(); + props.SetCompleted(); + } + FinishContinuations(); return true; } @@ -441,7 +441,7 @@ namespace System.Threading.Tasks bool success = TrySetResult(result); // Nobody else has had a chance to complete this Task yet, so we should succeed. - Debug.Assert(success); + Debug.Assert(success); } else { @@ -477,7 +477,7 @@ namespace System.Threading.Tasks { Debug.Assert(!IsWaitNotificationEnabledOrNotRanToCompletion, "Should only be used when the task completed successfully and there's no wait notification enabled"); - return m_result; + return m_result; } } @@ -539,7 +539,6 @@ namespace System.Threading.Tasks } return returnValue; - } // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder @@ -879,7 +878,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> /// has already been disposed. /// </exception> - public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,CancellationToken cancellationToken) + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, CancellationToken cancellationToken) { return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -942,7 +941,7 @@ namespace System.Threading.Tasks /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. /// </exception> - public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,TaskContinuationOptions continuationOptions) + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions) { return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } @@ -1014,7 +1013,7 @@ namespace System.Threading.Tasks out internalOptions); Task continuationTask = new ContinuationTaskFromResultTask<TResult>( - this, continuationAction, state, + this, continuationAction, state, creationOptions, internalOptions ); @@ -1229,7 +1228,7 @@ namespace System.Threading.Tasks out creationOptions, out internalOptions); - Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult, TNewResult>( this, continuationFunction, null, creationOptions, internalOptions ); @@ -1452,7 +1451,7 @@ namespace System.Threading.Tasks out creationOptions, out internalOptions); - Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult, TNewResult>( this, continuationFunction, state, creationOptions, internalOptions ); @@ -1467,7 +1466,7 @@ namespace System.Threading.Tasks #endregion #endregion - + /// <summary> /// Subscribes an <see cref="IObserver{TResult}"/> to receive notification of the final state of this <see cref="Task{TResult}"/>. /// </summary> @@ -1553,7 +1552,5 @@ namespace System.Threading.Tasks public int Id { get { return m_task.Id; } } public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } } public TaskStatus Status { get { return m_task.Status; } } - - } } |