diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs | 365 |
1 files changed, 184 insertions, 181 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs index 5d893c5cee..a8aac04936 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @@ -10,6 +10,7 @@ // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#nullable enable using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -130,13 +131,13 @@ namespace System.Threading.Tasks public class Task : IAsyncResult, IDisposable { [ThreadStatic] - internal static Task t_currentTask; // The currently executing task. + internal static Task? t_currentTask; // The currently executing task. internal static int s_taskIdCounter; //static counter used to generate unique task IDs private volatile int m_taskId; // this task's unique ID. initialized only if it is ever requested - internal Delegate m_action; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func. + internal Delegate? m_action; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func. // If m_action is set to null it will indicate that we operate in the // "externally triggered completion" mode, which is exclusively meant // for the signalling Task<TResult> (aka. promise). In this mode, @@ -145,12 +146,12 @@ namespace System.Threading.Tasks // But the event would now be signalled if Cancel() is called - internal object m_stateObject; // A state object that can be optionally supplied, passed to action. - internal TaskScheduler m_taskScheduler; // The task scheduler this task runs under. + internal object? m_stateObject; // A state object that can be optionally supplied, passed to action. + internal TaskScheduler? m_taskScheduler; // The task scheduler this task runs under. internal volatile int m_stateFlags; // SOS DumpAsync command depends on this name - private Task ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent + private Task? ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags // State constants for m_stateFlags; @@ -188,7 +189,7 @@ namespace System.Threading.Tasks // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel, // in that order. The logic arround this object assumes it will never regress to a previous state. - private volatile object m_continuationObject = null; // SOS DumpAsync command depends on this name + private volatile object? m_continuationObject = null; // SOS DumpAsync command depends on this name // m_continuationObject is set to this when the task completes. private static readonly object s_taskCompletionSentinel = new object(); @@ -199,7 +200,7 @@ namespace System.Threading.Tasks // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks. - private static Dictionary<int, Task> s_currentActiveTasks; + private static Dictionary<int, Task>? s_currentActiveTasks; // These methods are a way to access the dictionary both from this class and for other classes that also // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<> @@ -210,9 +211,9 @@ namespace System.Threading.Tasks LazyInitializer.EnsureInitialized(ref s_currentActiveTasks, () => new Dictionary<int, Task>()); int taskId = task.Id; - lock (s_currentActiveTasks) + lock (s_currentActiveTasks!) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 { - s_currentActiveTasks[taskId] = task; + s_currentActiveTasks![taskId] = task; // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } //always return true to keep signature as bool for backwards compatibility return true; @@ -238,17 +239,17 @@ namespace System.Threading.Tasks { // Additional context - internal ExecutionContext m_capturedContext; // The execution context to run the task within, if any. Only set from non-concurrent contexts. + internal ExecutionContext? m_capturedContext; // The execution context to run the task within, if any. Only set from non-concurrent contexts. // Completion fields (exceptions and event) - internal volatile ManualResetEventSlim m_completionEvent; // Lazily created if waiting is required. - internal volatile TaskExceptionHolder m_exceptionsHolder; // Tracks exceptions, if any have occurred + internal volatile ManualResetEventSlim? m_completionEvent; // Lazily created if waiting is required. + internal volatile TaskExceptionHolder? m_exceptionsHolder; // Tracks exceptions, if any have occurred // Cancellation fields (token, registration, and internally requested) internal CancellationToken m_cancellationToken; // Task's cancellation token, if it has one - internal Shared<CancellationTokenRegistration> m_cancellationRegistration; // Task's registration with the cancellation token + internal Shared<CancellationTokenRegistration>? m_cancellationRegistration; // Task's registration with the cancellation token internal volatile int m_internalCancellationRequested; // Its own field because multiple threads legally try to set it. // Parenting fields @@ -260,9 +261,9 @@ namespace System.Threading.Tasks internal volatile int m_completionCountdown = 1; // A list of child tasks that threw an exception (TCEs don't count), // but haven't yet been waited on by the parent, lazily initialized. - internal volatile List<Task> m_exceptionalChildren; + 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. @@ -295,7 +296,7 @@ namespace System.Threading.Tasks // This field will only be instantiated to some non-null value if any ContingentProperties need to be set. // This will be a ContingentProperties instance or a type derived from it - internal ContingentProperties m_contingentProperties; + internal ContingentProperties? m_contingentProperties; // Special internal constructor to create an already-completed task. // if canceled==true, create a Canceled task, or else create a RanToCompletion task. @@ -325,7 +326,7 @@ namespace System.Threading.Tasks // Special constructor for use with promise-style tasks. // Added promiseStyle parameter as an aid to the compiler to distinguish between (state,TCO) and // (action,TCO). It should always be true. - internal Task(object state, TaskCreationOptions creationOptions, bool promiseStyle) + internal Task(object? state, TaskCreationOptions creationOptions, bool promiseStyle) { Debug.Assert(promiseStyle, "Promise CTOR: promiseStyle was false"); @@ -339,7 +340,7 @@ namespace System.Threading.Tasks // Only set a parent if AttachedToParent is specified. if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) { - Task parent = Task.InternalCurrent; + Task? parent = Task.InternalCurrent; if (parent != null) { EnsureContingentPropertiesInitializedUnsafe().m_parent = parent; @@ -427,7 +428,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="action"/> argument is null. /// </exception> - public Task(Action<object> action, object state) + public Task(Action<object?> action, object? state) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 : this(action, state, null, default, TaskCreationOptions.None, InternalTaskOptions.None, null) { } @@ -444,7 +445,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(Action<object> action, object state, CancellationToken cancellationToken) + public Task(Action<object?> action, object? state, CancellationToken cancellationToken) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 : this(action, state, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null) { } @@ -465,7 +466,7 @@ namespace System.Threading.Tasks /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>. /// </exception> - public Task(Action<object> action, object state, TaskCreationOptions creationOptions) + public Task(Action<object?> action, object? state, TaskCreationOptions creationOptions) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), default, creationOptions, InternalTaskOptions.None, null) { } @@ -490,7 +491,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(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions) + public Task(Action<object?> action, object? state, CancellationToken cancellationToken, TaskCreationOptions creationOptions) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null) { } @@ -505,8 +506,8 @@ namespace System.Threading.Tasks /// <param name="scheduler">A task scheduler under which the task will run.</param> /// <param name="creationOptions">Options to control its execution.</param> /// <param name="internalOptions">Internal options to control its execution</param> - internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken, - TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) + internal Task(Delegate action, object? state, Task? parent, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler? scheduler) { if (action == null) { @@ -537,8 +538,8 @@ namespace System.Threading.Tasks /// <param name="cancellationToken">A CancellationToken for the Task.</param> /// <param name="creationOptions">Options to customize behavior of Task.</param> /// <param name="internalOptions">Internal options to customize behavior of Task.</param> - internal void TaskConstructorCore(Delegate action, object state, CancellationToken cancellationToken, - TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) + internal void TaskConstructorCore(Delegate? action, object? state, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler? scheduler) { m_action = action; m_stateObject = state; @@ -580,10 +581,10 @@ namespace System.Threading.Tasks // 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. - ContingentProperties props = m_contingentProperties; + ContingentProperties? props = m_contingentProperties; if (props != null) { - Task parent = props.m_parent; + Task? parent = props.m_parent; if (parent != null && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)) @@ -606,7 +607,7 @@ namespace System.Threading.Tasks /// Handles everything needed for associating a CancellationToken with a task which is being constructed. /// This method is meant to be called either from the TaskConstructorCore or from ContinueWithCore. /// </summary> - private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation) + private void AssignCancellationToken(CancellationToken cancellationToken, Task? antecedent, TaskContinuation? continuation) { // There is no need to worry about concurrency issues here because we are in the constructor path of the task -- // there should not be any race conditions to set m_contingentProperties at this point. @@ -634,16 +635,18 @@ namespace System.Threading.Tasks if (antecedent == null) { // if no antecedent was specified, use this task's reference as the cancellation state object - ctr = cancellationToken.UnsafeRegister(t => ((Task)t).InternalCancel(false), this); + ctr = cancellationToken.UnsafeRegister(t => ((Task)t!).InternalCancel(false), this); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } else { + Debug.Assert(continuation != null); + // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that // antecedent.RemoveCancellation(continuation) can be invoked. ctr = cancellationToken.UnsafeRegister(t => { - var tuple = (Tuple<Task, Task, TaskContinuation>)t; + var tuple = (Tuple<Task, Task, TaskContinuation>)t!; Task targetTask = tuple.Item1; Task antecedentTask = tuple.Item2; @@ -662,7 +665,7 @@ namespace System.Threading.Tasks { // If we have an exception related to our CancellationToken, then we need to subtract ourselves // from our parent before throwing it. - Task parent = m_contingentProperties?.m_parent; + Task? parent = m_contingentProperties?.m_parent; if ((parent != null) && ((Options & TaskCreationOptions.AttachedToParent) != 0) && ((parent.Options & TaskCreationOptions.DenyChildAttach) == 0)) @@ -678,8 +681,7 @@ namespace System.Threading.Tasks { get { - Delegate d = m_action; - return d != null ? d.Method.ToString() : "{null}"; + return m_action?.Method.ToString() ?? "{null}"; } } @@ -787,7 +789,7 @@ namespace System.Threading.Tasks /// <summary>Returns true if any of the supplied tasks require wait notification.</summary> /// <param name="tasks">The tasks to check.</param> /// <returns>true if any of the tasks require notification; otherwise, false.</returns> - internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task[] tasks) + internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task?[] tasks) { Debug.Assert(tasks != null, "Expected non-null array of tasks"); foreach (var task in tasks) @@ -871,8 +873,8 @@ namespace System.Threading.Tasks { m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED; - Task currentTask = Task.InternalCurrent; - Task parentTask = m_contingentProperties?.m_parent; + Task? currentTask = Task.InternalCurrent; + Task? parentTask = m_contingentProperties?.m_parent; TplEventSource.Log.TaskScheduled(ts.Id, currentTask == null ? 0 : currentTask.Id, this.Id, parentTask == null ? 0 : parentTask.Id, (int)this.Options); } @@ -1049,7 +1051,7 @@ namespace System.Threading.Tasks ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); } - InternalRunSynchronously(scheduler, waitForCompletion: true); + InternalRunSynchronously(scheduler!, waitForCompletion: true); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // @@ -1158,7 +1160,7 @@ namespace System.Threading.Tasks // Implicitly converts action to object and handles the meat of the StartNew() logic. internal static Task InternalStartNew( - Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, + Task? creatingTask, Delegate action, object? state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions) { // Validate arguments. @@ -1227,7 +1229,7 @@ namespace System.Threading.Tasks { get { - Task currentTask = InternalCurrent; + Task? currentTask = InternalCurrent; if (currentTask != null) return currentTask.Id; else @@ -1239,7 +1241,7 @@ namespace System.Threading.Tasks /// Gets the <see cref="Task">Task</see> instance currently executing, or /// null if none exists. /// </summary> - internal static Task InternalCurrent + internal static Task? InternalCurrent { get { return t_currentTask; } } @@ -1250,7 +1252,7 @@ namespace System.Threading.Tasks /// </summary> /// <param name="creationOptions">The options to check.</param> /// <returns>The current task if there is one and if AttachToParent is in the options; otherwise, null.</returns> - internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions) + internal static Task? InternalCurrentIfAttached(TaskCreationOptions creationOptions) { return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null; } @@ -1267,11 +1269,11 @@ namespace System.Threading.Tasks /// or in accesses to the <see cref="Exception"/> property. Any exceptions not observed by the time /// the Task instance is garbage collected will be propagated on the finalizer thread. /// </remarks> - public AggregateException Exception + public AggregateException? Exception { get { - AggregateException e = null; + AggregateException? e = null; // If you're faulted, retrieve the exception(s) if (IsFaulted) e = GetExceptions(false); @@ -1355,7 +1357,7 @@ namespace System.Threading.Tasks /// <returns>The initialized contingent properties object.</returns> internal ContingentProperties EnsureContingentPropertiesInitialized() { - return LazyInitializer.EnsureInitialized(ref m_contingentProperties, () => new ContingentProperties()); + return LazyInitializer.EnsureInitialized(ref m_contingentProperties, () => new ContingentProperties())!; // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } /// <summary> @@ -1460,7 +1462,7 @@ namespace System.Threading.Tasks /// Gets the state object supplied when the <see cref="Task">Task</see> was created, /// or null if none was supplied. /// </summary> - public object AsyncState + public object? AsyncState { get { return m_stateObject; } } @@ -1480,7 +1482,7 @@ namespace System.Threading.Tasks /// <summary> /// Provides access to the TaskScheduler responsible for executing this Task. /// </summary> - internal TaskScheduler ExecutingTaskScheduler + internal TaskScheduler? ExecutingTaskScheduler { get { return m_taskScheduler; } } @@ -1524,13 +1526,13 @@ namespace System.Threading.Tasks } } - return contingentProps.m_completionEvent; + return contingentProps.m_completionEvent!; // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } } /// <summary> - /// The property formerly known as IsFaulted. + /// Whether an exception has been stored into the task. /// </summary> internal bool ExceptionRecorded { @@ -1563,7 +1565,7 @@ namespace System.Threading.Tasks /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context /// </summary> - internal ExecutionContext CapturedContext + internal ExecutionContext? CapturedContext { get { @@ -1716,6 +1718,7 @@ namespace System.Threading.Tasks if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0) { //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor + Debug.Assert(m_action != null, "Must have a delegate to be in ScheduleAndStart"); AsyncCausalityTracer.TraceOperationCreation(this, "Task: " + m_action.Method.Name); } @@ -1813,7 +1816,7 @@ namespace System.Threading.Tasks lock (props) { - props.m_exceptionsHolder.Add(exceptionObject, representsCancellation); + props.m_exceptionsHolder!.Add(exceptionObject, representsCancellation); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } } @@ -1823,7 +1826,7 @@ namespace System.Threading.Tasks /// </summary> /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param> /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns> - private AggregateException GetExceptions(bool includeTaskCanceledExceptions) + private AggregateException? GetExceptions(bool includeTaskCanceledExceptions) { // // WARNING: The Task/Task<TResult>/TaskCompletionSource classes @@ -1861,7 +1864,7 @@ namespace System.Threading.Tasks // // We'll lazily create a TCE if the task has been canceled. - Exception canceledException = null; + Exception? canceledException = null; if (includeTaskCanceledExceptions && IsCanceled) { // Backcompat: @@ -1877,7 +1880,7 @@ namespace System.Threading.Tasks { // There are exceptions; get the aggregate and optionally add the canceled // exception to the aggregate (if applicable). - Debug.Assert(m_contingentProperties != null); // ExceptionRecorded ==> m_contingentProperties != null + Debug.Assert(m_contingentProperties != null && m_contingentProperties.m_exceptionsHolder != null, "ExceptionRecorded should imply this"); // No need to lock around this, as other logic prevents the consumption of exceptions // before they have been completely processed. @@ -1898,13 +1901,13 @@ namespace System.Threading.Tasks bool exceptionsAvailable = IsFaulted && ExceptionRecorded; Debug.Assert(exceptionsAvailable, "Must only be used when the task has faulted with exceptions."); return exceptionsAvailable ? - m_contingentProperties.m_exceptionsHolder.GetExceptionDispatchInfos() : + m_contingentProperties!.m_exceptionsHolder!.GetExceptionDispatchInfos() : new ReadOnlyCollection<ExceptionDispatchInfo>(new ExceptionDispatchInfo[0]); } /// <summary>Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task.</summary> /// <returns>The ExceptionDispatchInfo. May be null if no OCE was stored for the task.</returns> - internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo() + internal ExceptionDispatchInfo? GetCancellationExceptionDispatchInfo() { Debug.Assert(IsCanceled, "Must only be used when the task has canceled."); return Volatile.Read(ref m_contingentProperties)?.m_exceptionsHolder?.GetCancellationExceptionDispatchInfo(); // may be null @@ -1917,7 +1920,7 @@ namespace System.Threading.Tasks { Debug.Assert(IsCompleted, "ThrowIfExceptional(): Expected IsCompleted == true"); - Exception exception = GetExceptions(includeTaskCanceledExceptions); + Exception? exception = GetExceptions(includeTaskCanceledExceptions); if (exception != null) { UpdateExceptionObservedStatus(); @@ -1928,7 +1931,7 @@ namespace System.Threading.Tasks /// <summary>Throws the exception on the ThreadPool.</summary> /// <param name="exception">The exception to propagate.</param> /// <param name="targetContext">The target context on which to propagate the exception. Null to use the ThreadPool.</param> - internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) + internal static void ThrowAsync(Exception exception, SynchronizationContext? targetContext) { // Capture the exception into an ExceptionDispatchInfo so that its // stack trace and Watson bucket info will be preserved @@ -1940,7 +1943,7 @@ namespace System.Threading.Tasks try { // Post the throwing of the exception to that context, and return. - targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi); + targetContext.Post(state => ((ExceptionDispatchInfo)state!).Throw(), edi); return; } catch (Exception postException) @@ -1962,7 +1965,7 @@ namespace System.Threading.Tasks #endif // Propagate the exception(s) on the ThreadPool - ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state).Throw(), edi); + ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state!).Throw(), edi); #endif // CORERT } @@ -1980,7 +1983,7 @@ namespace System.Threading.Tasks /// </summary> internal void UpdateExceptionObservedStatus() { - Task parent = m_contingentProperties?.m_parent; + Task? parent = m_contingentProperties?.m_parent; if ((parent != null) && ((Options & TaskCreationOptions.AttachedToParent) != 0) && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) @@ -2048,7 +2051,7 @@ namespace System.Threading.Tasks } else { - ContingentProperties props = m_contingentProperties; + ContingentProperties props = m_contingentProperties!; // Count of 1 => either all children finished, or there were none. Safe to complete ourselves // without paying the price of an Interlocked.Decrement. @@ -2075,7 +2078,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.m_exceptionalChildren; + List<Task>? exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { lock (exceptionalChildren) @@ -2096,7 +2099,7 @@ namespace System.Threading.Tasks // 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); + ContingentProperties? cp = Volatile.Read(ref m_contingentProperties); if (cp != null) { AddExceptionsFromChildren(cp); @@ -2170,7 +2173,7 @@ namespace System.Threading.Tasks // continuations hold onto the task, and therefore are keeping it alive. m_action = null; - ContingentProperties cp = m_contingentProperties; + ContingentProperties? cp = m_contingentProperties; if (cp != null) { // Similarly, null out any ExecutionContext we may have captured, @@ -2188,7 +2191,7 @@ namespace System.Threading.Tasks internal void NotifyParentIfPotentiallyAttachedTask() { - Task parent = m_contingentProperties?.m_parent; + Task? parent = m_contingentProperties?.m_parent; if (parent != null && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0) @@ -2213,7 +2216,7 @@ namespace System.Threading.Tasks if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent) { // Lazily initialize the child exception list - if (props.m_exceptionalChildren == null) + if (props!.m_exceptionalChildren == null) { Interlocked.CompareExchange(ref props.m_exceptionalChildren, new List<Task>(), null); } @@ -2222,7 +2225,7 @@ namespace System.Threading.Tasks // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect // ourselves by caching m_exceptionChildren in a local variable. - List<Task> tmp = props.m_exceptionalChildren; + List<Task>? tmp = props.m_exceptionalChildren; if (tmp != null) { lock (tmp) @@ -2232,7 +2235,7 @@ namespace System.Threading.Tasks } } - if (Interlocked.Decrement(ref props.m_completionCountdown) == 0) + if (Interlocked.Decrement(ref props!.m_completionCountdown) == 0) { // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself. // So we need to invoke the final finish stage. @@ -2253,7 +2256,7 @@ namespace System.Threading.Tasks // 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. - List<Task> exceptionalChildren = props.m_exceptionalChildren; + List<Task>? exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { @@ -2268,7 +2271,7 @@ namespace System.Threading.Tasks Debug.Assert(task.IsCompleted, "Expected all tasks in list to be completed"); if (task.IsFaulted && !task.IsExceptionObservedByParent) { - TaskExceptionHolder exceptionHolder = Volatile.Read(ref task.m_contingentProperties).m_exceptionsHolder; + TaskExceptionHolder? exceptionHolder = Volatile.Read(ref task.m_contingentProperties)!.m_exceptionsHolder; Debug.Assert(exceptionHolder != null); // No locking necessary since child task is finished adding exceptions @@ -2322,7 +2325,7 @@ namespace System.Threading.Tasks /// </summary> internal virtual void ExecuteFromThreadPool(Thread threadPoolThread) => ExecuteEntryUnsafe(threadPoolThread); - internal void ExecuteEntryUnsafe(Thread threadPoolThread) // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent + internal void ExecuteEntryUnsafe(Thread? threadPoolThread) // 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; @@ -2350,10 +2353,10 @@ namespace System.Threading.Tasks } // A trick so we can refer to the TLS slot with a byref. - private void ExecuteWithThreadLocal(ref Task currentTaskSlot, Thread threadPoolThread = null) + private void ExecuteWithThreadLocal(ref Task? currentTaskSlot, Thread? threadPoolThread = null) { // Remember the current task so we can restore it after running, and then - Task previousTask = currentTaskSlot; + Task? previousTask = currentTaskSlot; // ETW event for Task Started var log = TplEventSource.Log; @@ -2365,7 +2368,7 @@ namespace System.Threading.Tasks EventSource.SetCurrentThreadActivityId(TplEventSource.CreateGuidForTaskID(this.Id), out savedActivityID); // previousTask holds the actual "current task" we want to report in the event if (previousTask != null) - log.TaskStarted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id); + log.TaskStarted(previousTask.m_taskScheduler!.Id, previousTask.Id, this.Id); else log.TaskStarted(TaskScheduler.Current.Id, 0, this.Id); } @@ -2382,7 +2385,7 @@ namespace System.Threading.Tasks // Execute the task body try { - ExecutionContext ec = CapturedContext; + ExecutionContext? ec = CapturedContext; if (ec == null) { // No context, just run the task directly. @@ -2421,7 +2424,7 @@ namespace System.Threading.Tasks { // previousTask holds the actual "current task" we want to report in the event if (previousTask != null) - log.TaskCompleted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id, IsFaulted); + log.TaskCompleted(previousTask.m_taskScheduler!.Id, previousTask.Id, this.Id, IsFaulted); else log.TaskCompleted(TaskScheduler.Current.Id, 0, this.Id, IsFaulted); @@ -2451,7 +2454,7 @@ namespace System.Threading.Tasks return; } - if (m_action is Action<object> actionWithState) + if (m_action is Action<object?> actionWithState) { actionWithState(m_stateObject); return; @@ -2469,7 +2472,7 @@ namespace System.Threading.Tasks Debug.Assert(unhandledException != null); if (unhandledException is OperationCanceledException exceptionAsOce && IsCancellationRequested && - m_contingentProperties.m_cancellationToken == exceptionAsOce.CancellationToken) + m_contingentProperties!.m_cancellationToken == exceptionAsOce.CancellationToken) { // All conditions are satisfied for us to go into canceled state in Finish(). // Mark the acknowledgement. The exception is also stored to enable it to be @@ -2526,7 +2529,7 @@ namespace System.Threading.Tasks // Create the best AwaitTaskContinuation object given the request. // If this remains null by the end of the function, we can use the // continuationAction directly without wrapping it. - TaskContinuation tc = null; + TaskContinuation? tc = null; // If the user wants the continuation to run on the current "context" if there is one... if (continueOnCapturedContext) @@ -2605,7 +2608,7 @@ namespace System.Threading.Tasks // fall back to using the state machine's delegate. if (continueOnCapturedContext) { - SynchronizationContext syncCtx = SynchronizationContext.Current; + SynchronizationContext? syncCtx = SynchronizationContext.Current; if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) { var tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, stateMachineBox.MoveNextAction, flowExecutionContext: false); @@ -2617,7 +2620,7 @@ namespace System.Threading.Tasks } else { - TaskScheduler scheduler = TaskScheduler.InternalCurrent; + TaskScheduler? scheduler = TaskScheduler.InternalCurrent; if (scheduler != null && scheduler != TaskScheduler.Default) { var tc = new TaskSchedulerAwaitTaskContinuation(scheduler, stateMachineBox.MoveNextAction, flowExecutionContext: false); @@ -2846,9 +2849,9 @@ namespace System.Threading.Tasks bool etwIsEnabled = log.IsEnabled(); if (etwIsEnabled) { - Task currentTask = Task.InternalCurrent; + Task? currentTask = Task.InternalCurrent; log.TaskWaitBegin( - (currentTask != null ? currentTask.m_taskScheduler.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0), + (currentTask != null ? currentTask.m_taskScheduler!.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0), this.Id, TplEventSource.TaskWaitBehavior.Synchronous, 0); } @@ -2877,10 +2880,10 @@ namespace System.Threading.Tasks // ETW event for Task Wait End if (etwIsEnabled) { - Task currentTask = Task.InternalCurrent; + Task? currentTask = Task.InternalCurrent; if (currentTask != null) { - log.TaskWaitEnd(currentTask.m_taskScheduler.Id, currentTask.Id, this.Id); + log.TaskWaitEnd(currentTask.m_taskScheduler!.Id, currentTask.Id, this.Id); } else { @@ -2999,12 +3002,12 @@ namespace System.Threading.Tasks bool bPopSucceeded = false; bool mustCleanup = false; - TaskSchedulerException tse = null; + TaskSchedulerException? tse = null; // If started, and running in a task context, we can try to pop the chore. if ((m_stateFlags & TASK_STATE_STARTED) != 0) { - TaskScheduler ts = m_taskScheduler; + TaskScheduler? ts = m_taskScheduler; try { @@ -3095,7 +3098,7 @@ namespace System.Threading.Tasks RecordInternalCancellationRequest(); Debug.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task"); - Debug.Assert(m_contingentProperties.m_cancellationToken == default); + Debug.Assert(m_contingentProperties!.m_cancellationToken == default); // Store the supplied cancellation token as this task's token. // Waiting on this task will then result in an OperationCanceledException containing this token. @@ -3108,7 +3111,7 @@ namespace System.Threading.Tasks // Breaks out logic for recording a cancellation request // This overload should only be used for promise tasks where no cancellation token // was supplied when the task was created. - internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object cancellationException) + internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object? cancellationException) { RecordInternalCancellationRequest(tokenToRecord); @@ -3184,7 +3187,7 @@ namespace System.Threading.Tasks TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION, TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) { - ContingentProperties props = m_contingentProperties; + ContingentProperties? props = m_contingentProperties; if (props != null) { NotifyParentIfPotentiallyAttachedTask(); @@ -3252,7 +3255,7 @@ namespace System.Threading.Tasks // If the tokenToRecord is not None, it will be stored onto the task. // If the OperationCanceledException is not null, it will be stored into the task's exception holder. // This method is only valid for promise tasks. - internal bool TrySetCanceled(CancellationToken tokenToRecord, object cancellationException) + internal bool TrySetCanceled(CancellationToken tokenToRecord, object? cancellationException) { Debug.Assert(m_action == null, "Task<T>.TrySetCanceled(): non-null m_action"); Debug.Assert( @@ -3296,7 +3299,7 @@ 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); + object? continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel); if (continuationObject != null) { RunContinuations(continuationObject); @@ -3352,7 +3355,7 @@ namespace System.Threading.Tasks } // Not a single; it must be a list. - List<object> continuations = (List<object>)continuationObject; + List<object?> continuations = (List<object?>)continuationObject; // // Begin processing of continuation list @@ -3384,7 +3387,7 @@ namespace System.Threading.Tasks // and Action delegates, which are all by default implicitly synchronous. for (int i = 0; i < continuationCount; i++) { - object currentContinuation = continuations[i]; + object? currentContinuation = continuations[i]; if (currentContinuation == null) { continue; @@ -3611,13 +3614,13 @@ namespace System.Threading.Tasks CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions); Task continuationTask = new ContinuationTaskFromTask( - this, continuationAction, null, + this, continuationAction!, null, // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. - ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + ContinueWithCore(continuationTask, scheduler!, cancellationToken, continuationOptions); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 return continuationTask; } @@ -3642,7 +3645,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="continuationAction"/> argument is null. /// </exception> - public Task ContinueWith(Action<Task, object> continuationAction, object state) + public Task ContinueWith(Action<Task, object?> continuationAction, object? state) { return ContinueWith(continuationAction, state, TaskScheduler.Current, default, TaskContinuationOptions.None); } @@ -3668,7 +3671,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, object> continuationAction, object state, CancellationToken cancellationToken) + public Task ContinueWith(Action<Task, object?> continuationAction, object? state, CancellationToken cancellationToken) { return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -3696,7 +3699,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="scheduler"/> argument is null. /// </exception> - public Task ContinueWith(Action<Task, object> continuationAction, object state, TaskScheduler scheduler) + public Task ContinueWith(Action<Task, object?> continuationAction, object? state, TaskScheduler scheduler) { return ContinueWith(continuationAction, state, scheduler, default, TaskContinuationOptions.None); } @@ -3730,7 +3733,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, object> continuationAction, object state, TaskContinuationOptions continuationOptions) + public Task ContinueWith(Action<Task, object?> continuationAction, object? state, TaskContinuationOptions continuationOptions) { return ContinueWith(continuationAction, state, TaskScheduler.Current, default, continuationOptions); } @@ -3774,14 +3777,14 @@ 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, object> continuationAction, object state, CancellationToken cancellationToken, + public Task ContinueWith(Action<Task, object?> continuationAction, object? state, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions); } // Same as the above overload, just with a stack mark parameter. - private Task ContinueWith(Action<Task, object> continuationAction, object state, TaskScheduler scheduler, + private Task ContinueWith(Action<Task, object?> continuationAction, object? state, TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null action @@ -3801,13 +3804,13 @@ namespace System.Threading.Tasks CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions); Task continuationTask = new ContinuationTaskFromTask( - this, continuationAction, state, + this, continuationAction!, state, // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. - ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + ContinueWithCore(continuationTask, scheduler!, cancellationToken, continuationOptions); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 return continuationTask; } @@ -4004,13 +4007,13 @@ namespace System.Threading.Tasks CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions); Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>( - this, continuationFunction, null, + this, continuationFunction!, null, // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. - ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + ContinueWithCore(continuationTask, scheduler!, cancellationToken, continuationOptions); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 return continuationTask; } @@ -4038,7 +4041,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="continuationFunction"/> argument is null. /// </exception> - public Task<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state) + public Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state) { return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default, TaskContinuationOptions.None); @@ -4069,7 +4072,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<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken) + public Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state, CancellationToken cancellationToken) { return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -4100,7 +4103,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="scheduler"/> argument is null. /// </exception> - public Task<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler) + public Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state, TaskScheduler scheduler) { return ContinueWith<TResult>(continuationFunction, state, scheduler, default, TaskContinuationOptions.None); } @@ -4137,7 +4140,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<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions) + public Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state, TaskContinuationOptions continuationOptions) { return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default, continuationOptions); } @@ -4184,14 +4187,14 @@ 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<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken, + public Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions); } // Same as the above overload, just with a stack mark parameter. - private Task<TResult> ContinueWith<TResult>(Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler, + private Task<TResult> ContinueWith<TResult>(Func<Task, object?, TResult> continuationFunction, object? state, TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions) { // Throw on continuation with null function @@ -4211,13 +4214,13 @@ namespace System.Threading.Tasks CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions); Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>( - this, continuationFunction, state, + this, continuationFunction!, state, // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 creationOptions, internalOptions ); // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. - ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + ContinueWithCore(continuationTask, scheduler!, cancellationToken, continuationOptions); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 return continuationTask; } @@ -4386,13 +4389,13 @@ namespace System.Threading.Tasks { Debug.Assert(tc != null, "Expected non-null tc object in AddTaskContinuationComplex"); - object oldValue = m_continuationObject; + object? oldValue = m_continuationObject; // Logic for the case where we were previously storing a single continuation - if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>))) + if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object?>))) { // Construct a new TaskContinuation list - List<object> newList = new List<object>(); + List<object?> newList = new List<object?>(); // Add in the old single value newList.Add(oldValue); @@ -4407,7 +4410,7 @@ namespace System.Threading.Tasks // m_continuationObject is guaranteed at this point to be either a List or // s_taskCompletionSentinel. - List<object> list = m_continuationObject as List<object>; + List<object?>? list = m_continuationObject as List<object?>; Debug.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel), "Expected m_continuationObject to be list or sentinel"); @@ -4469,21 +4472,21 @@ namespace System.Threading.Tasks { // We need to snap a local reference to m_continuations since reading a volatile object is more costly. // Also to prevent the value to be changed as result of a race condition with another method. - object continuationsLocalRef = m_continuationObject; + object? continuationsLocalRef = m_continuationObject; // Task is completed. Nothing to do here. if (continuationsLocalRef == s_taskCompletionSentinel) return; - if (!(continuationsLocalRef is List<object> continuationsLocalListRef)) + if (!(continuationsLocalRef is List<object?> continuationsLocalListRef)) { // This is not a list. If we have a single object (the one we want to remove) we try to replace it with an empty list. // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic. - if (Interlocked.CompareExchange(ref m_continuationObject, new List<object>(), continuationObject) != continuationObject) + if (Interlocked.CompareExchange(ref m_continuationObject, new List<object?>(), continuationObject) != continuationObject) { // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel. // So we should try to get a list one more time - continuationsLocalListRef = m_continuationObject as List<object>; + continuationsLocalListRef = m_continuationObject as List<object?>; } else { @@ -4711,9 +4714,9 @@ namespace System.Threading.Tasks // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths // - List<Exception> exceptions = null; - List<Task> waitedOnTaskList = null; - List<Task> notificationTasks = null; + List<Exception>? exceptions = null; + List<Task>? waitedOnTaskList = null; + List<Task>? notificationTasks = null; // If any of the waited-upon tasks end as Faulted or Canceled, set these to true. bool exceptionSeen = false, cancellationSeen = false; @@ -4721,7 +4724,7 @@ namespace System.Threading.Tasks bool returnValue = true; // Collects incomplete tasks in "waitedOnTaskList" - for (int i = tasks.Length - 1; i >= 0; i--) + for (int i = tasks!.Length - 1; i >= 0; i--) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { Task task = tasks[i]; @@ -4730,7 +4733,7 @@ namespace System.Threading.Tasks ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks); } - bool taskIsCompleted = task.IsCompleted; + bool taskIsCompleted = task!.IsCompleted; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 if (!taskIsCompleted) { // try inlining the task only if we have an infinite timeout and an empty cancellation token @@ -4819,7 +4822,7 @@ namespace System.Threading.Tasks /// <param name="item">The item to add.</param> /// <param name="list">The list.</param> /// <param name="initSize">The size to which to initialize the list if the list is null.</param> - private static void AddToList<T>(T item, ref List<T> list, int initSize) + private static void AddToList<T>(T item, ref List<T>? list, int initSize) { if (list == null) list = new List<T>(initSize); list.Add(item); @@ -4897,9 +4900,9 @@ namespace System.Threading.Tasks /// If the completed task is canceled or it has other exceptions, here we will add those /// into the passed in exception list (which will be lazily initialized here). /// </summary> - internal static void AddExceptionsForCompletedTask(ref List<Exception> exceptions, Task t) + internal static void AddExceptionsForCompletedTask(ref List<Exception>? exceptions, Task t) { - AggregateException ex = t.GetExceptions(true); + AggregateException? ex = t.GetExceptions(true); if (ex != null) { // make sure the task's exception observed status is set appropriately @@ -5086,7 +5089,7 @@ namespace System.Threading.Tasks // Make a pass through the loop to check for any tasks that may have // already been completed, and to verify that no tasks are null. - for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++) + for (int taskIndex = 0; taskIndex < tasks!.Length; taskIndex++) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { Task task = tasks[taskIndex]; @@ -5095,7 +5098,7 @@ namespace System.Threading.Tasks ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks); } - if (signaledTaskIndex == -1 && task.IsCompleted) + if (signaledTaskIndex == -1 && task!.IsCompleted) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { // We found our first completed task. Store it, but we can't just return here, // as we still need to validate the whole array for nulls. @@ -5147,7 +5150,7 @@ namespace System.Threading.Tasks if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); var task = new Task(); - bool succeeded = task.TrySetException(exception); + bool succeeded = task.TrySetException(exception!); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 Debug.Assert(succeeded, "This should always succeed on a new task."); return task; } @@ -5161,7 +5164,7 @@ namespace System.Threading.Tasks if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); var task = new Task<TResult>(); - bool succeeded = task.TrySetException(exception); + bool succeeded = task.TrySetException(exception!); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 Debug.Assert(succeeded, "This should always succeed on a new task."); return task; } @@ -5184,7 +5187,7 @@ namespace System.Threading.Tasks { if (!cancellationToken.IsCancellationRequested) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.cancellationToken); - return new Task<TResult>(true, default, TaskCreationOptions.None, cancellationToken); + return new Task<TResult>(true, default!, TaskCreationOptions.None, cancellationToken); // TODO-NULLABLE-GENERIC } /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified exception.</summary> @@ -5292,7 +5295,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="function"/> parameter was null. /// </exception> - public static Task Run(Func<Task> function) + public static Task Run(Func<Task?> function) { return Run(function, default); } @@ -5311,7 +5314,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ObjectDisposedException"> /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed. /// </exception> - public static Task Run(Func<Task> function, CancellationToken cancellationToken) + public static Task Run(Func<Task?> function, CancellationToken cancellationToken) { // Check arguments if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function); @@ -5321,7 +5324,7 @@ namespace System.Threading.Tasks return Task.FromCanceled(cancellationToken); // Kick off initial Task, which will call the user-supplied function and yield a Task. - Task<Task> task1 = Task<Task>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + Task<Task?> task1 = Task<Task?>.Factory.StartNew(function!, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 // Create a promise-style Task to be used as a proxy for the operation // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation. @@ -5340,7 +5343,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="function"/> parameter was null. /// </exception> - public static Task<TResult> Run<TResult>(Func<Task<TResult>> function) + public static Task<TResult> Run<TResult>(Func<Task<TResult>?> function) { return Run(function, default); } @@ -5356,7 +5359,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ArgumentNullException"> /// The <paramref name="function"/> parameter was null. /// </exception> - public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken) + public static Task<TResult> Run<TResult>(Func<Task<TResult>?> function, CancellationToken cancellationToken) { // Check arguments if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function); @@ -5366,7 +5369,7 @@ namespace System.Threading.Tasks return Task.FromCanceled<TResult>(cancellationToken); // Kick off initial Task, which will call the user-supplied function and yield a Task. - Task<Task<TResult>> task1 = Task<Task<TResult>>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + Task<Task<TResult>?> task1 = Task<Task<TResult>?>.Factory.StartNew(function!, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 // Create a promise-style Task to be used as a proxy for the operation // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation. @@ -5475,7 +5478,7 @@ namespace System.Threading.Tasks /// <summary>Task that also stores the completion closure and logic for Task.Delay implementation.</summary> private class DelayPromise : Task { - private readonly TimerQueueTimer _timer; + private readonly TimerQueueTimer? _timer; internal DelayPromise(int millisecondsDelay) { @@ -5489,7 +5492,7 @@ namespace System.Threading.Tasks if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout { - _timer = new TimerQueueTimer(state => ((DelayPromise)state).CompleteTimedOut(), this, (uint)millisecondsDelay, Timeout.UnsignedInfinite, flowExecutionContext: false); + _timer = new TimerQueueTimer(state => ((DelayPromise)state!).CompleteTimedOut(), this, (uint)millisecondsDelay, Timeout.UnsignedInfinite, flowExecutionContext: false); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 if (IsCanceled) { // Handle rare race condition where cancellation occurs prior to our having created and stored the timer, in which case @@ -5528,7 +5531,7 @@ namespace System.Threading.Tasks Debug.Assert(token.CanBeCanceled); _token = token; - _registration = token.UnsafeRegister(state => ((DelayPromiseWithCancellation)state).CompleteCanceled(), this); + _registration = token.UnsafeRegister(state => ((DelayPromiseWithCancellation)state!).CompleteCanceled(), this); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 } private void CompleteCanceled() @@ -5593,7 +5596,7 @@ namespace System.Threading.Tasks foreach (var task in tasks) { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - taskArray[index++] = task; + taskArray[index++] = task!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } return InternalWhenAll(taskArray); } @@ -5601,10 +5604,10 @@ namespace System.Threading.Tasks // Do some argument checking and convert tasks to a List (and later an array). if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); List<Task> taskList = new List<Task>(); - foreach (Task task in tasks) + foreach (Task task in tasks!) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - taskList.Add(task); + taskList.Add(task!); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // Delegate the rest to InternalWhenAll() @@ -5643,7 +5646,7 @@ namespace System.Threading.Tasks // Do some argument checking and make a defensive copy of the tasks array if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); - int taskCount = tasks.Length; + int taskCount = tasks!.Length; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 if (taskCount == 0) return InternalWhenAll(tasks); // Small optimization in the case of an empty array. Task[] tasksCopy = new Task[taskCount]; @@ -5651,7 +5654,7 @@ namespace System.Threading.Tasks { Task task = tasks[i]; if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - tasksCopy[i] = task; + tasksCopy[i] = task!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // The rest can be delegated to InternalWhenAll() @@ -5686,7 +5689,7 @@ namespace System.Threading.Tasks /// Stores all of the constituent tasks. Tasks clear themselves out of this /// array as they complete, but only if they don't have their wait notification bit set. /// </summary> - private readonly Task[] m_tasks; + private readonly Task?[] m_tasks; /// <summary>The number of tasks remaining to complete.</summary> private int m_count; @@ -5720,8 +5723,8 @@ namespace System.Threading.Tasks if (Interlocked.Decrement(ref m_count) == 0) { // Set up some accounting variables - List<ExceptionDispatchInfo> observedExceptions = null; - Task canceledTask = null; + List<ExceptionDispatchInfo>? observedExceptions = null; + Task? canceledTask = null; // Loop through antecedents: // If any one of them faults, the result will be faulted @@ -5837,7 +5840,7 @@ namespace System.Threading.Tasks foreach (var task in tasks) { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - taskArray[index++] = task; + taskArray[index++] = task!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } return InternalWhenAll<TResult>(taskArray); } @@ -5845,10 +5848,10 @@ namespace System.Threading.Tasks // Do some argument checking and convert tasks into a List (later an array) if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); List<Task<TResult>> taskList = new List<Task<TResult>>(); - foreach (Task<TResult> task in tasks) + foreach (Task<TResult> task in tasks!) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - taskList.Add(task); + taskList.Add(task!); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // Delegate the rest to InternalWhenAll<TResult>(). @@ -5890,7 +5893,7 @@ namespace System.Threading.Tasks // Do some argument checking and make a defensive copy of the tasks array if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); - int taskCount = tasks.Length; + int taskCount = tasks!.Length; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 if (taskCount == 0) return InternalWhenAll<TResult>(tasks); // small optimization in the case of an empty task array Task<TResult>[] tasksCopy = new Task<TResult>[taskCount]; @@ -5898,7 +5901,7 @@ namespace System.Threading.Tasks { Task<TResult> task = tasks[i]; if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - tasksCopy[i] = task; + tasksCopy[i] = task!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // Delegate the rest to InternalWhenAll<TResult>() @@ -5925,7 +5928,7 @@ namespace System.Threading.Tasks /// Stores all of the constituent tasks. Tasks clear themselves out of this /// array as they complete, but only if they don't have their wait notification bit set. /// </summary> - private readonly Task<T>[] m_tasks; + private readonly Task<T>?[] m_tasks; /// <summary>The number of tasks remaining to complete.</summary> private int m_count; @@ -5961,8 +5964,8 @@ namespace System.Threading.Tasks { // Set up some accounting variables T[] results = new T[m_tasks.Length]; - List<ExceptionDispatchInfo> observedExceptions = null; - Task canceledTask = null; + List<ExceptionDispatchInfo>? observedExceptions = null; + Task? canceledTask = null; // Loop through antecedents: // If any one of them faults, the result will be faulted @@ -5970,7 +5973,7 @@ namespace System.Threading.Tasks // If none fault or are canceled, then result will be RanToCompletion for (int i = 0; i < m_tasks.Length; i++) { - Task<T> task = m_tasks[i]; + Task<T>? task = m_tasks[i]; Debug.Assert(task != null, "Constituent task in WhenAll should never be null"); if (task.IsFaulted) @@ -6057,7 +6060,7 @@ namespace System.Threading.Tasks public static Task<Task> WhenAny(params Task[] tasks) { if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); - if (tasks.Length == 0) + if (tasks!.Length == 0) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); } @@ -6070,7 +6073,7 @@ namespace System.Threading.Tasks { Task task = tasks[i]; if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - tasksCopy[i] = task; + tasksCopy[i] = task!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } // Previously implemented CommonCWAnyLogic() can handle the rest @@ -6099,10 +6102,10 @@ namespace System.Threading.Tasks // Make a defensive copy, as the user may manipulate the tasks collection // after we return but before the WhenAny asynchronously completes. List<Task> taskList = new List<Task>(); - foreach (Task task in tasks) + foreach (Task task in tasks!) // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); - taskList.Add(task); + taskList.Add(task!); // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538 } if (taskList.Count == 0) @@ -6183,7 +6186,7 @@ namespace System.Threading.Tasks #if PROJECTN [DependencyReductionRoot] #endif - internal virtual Delegate[] GetDelegateContinuationsForDebugger() + internal virtual Delegate[]? GetDelegateContinuationsForDebugger() { //Avoid an infinite loop by making sure the continuation object is not a reference to istelf. if (m_continuationObject != this) @@ -6192,7 +6195,7 @@ namespace System.Threading.Tasks return null; } - private static Delegate[] GetDelegatesFromContinuationObject(object continuationObject) + private static Delegate[]? GetDelegatesFromContinuationObject(object? continuationObject) { if (continuationObject != null) { @@ -6209,7 +6212,7 @@ namespace System.Threading.Tasks if (continuationObject is Task continuationTask) { Debug.Assert(continuationTask.m_action == null); - Delegate[] delegates = continuationTask.GetDelegateContinuationsForDebugger(); + Delegate[]? delegates = continuationTask.GetDelegateContinuationsForDebugger(); if (delegates != null) return delegates; } @@ -6221,10 +6224,10 @@ namespace System.Threading.Tasks return new Delegate[] { new Action<Task>(singleCompletionAction.Invoke) }; } - if (continuationObject is List<object> continuationList) + if (continuationObject is List<object?> continuationList) { List<Delegate> result = new List<Delegate>(); - foreach (object obj in continuationList) + foreach (object? obj in continuationList) { var innerDelegates = GetDelegatesFromContinuationObject(obj); if (innerDelegates != null) @@ -6248,10 +6251,10 @@ namespace System.Threading.Tasks [DependencyReductionRoot] #endif //Do not remove: VS debugger calls this API directly using func-eval to populate data in the tasks window - private static Task GetActiveTaskFromId(int taskId) + private static Task? GetActiveTaskFromId(int taskId) { - Task task = null; - s_currentActiveTasks.TryGetValue(taskId, out task); + Task? task = null; + s_currentActiveTasks?.TryGetValue(taskId, out task); return task; } } @@ -6283,9 +6286,9 @@ namespace System.Threading.Tasks m_task = task; } - public object AsyncState { get { return m_task.AsyncState; } } + public object? AsyncState { get { return m_task.AsyncState; } } public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } } - public Exception Exception { get { return m_task.Exception; } } + public Exception? Exception { get { return m_task.Exception; } } public int Id { get { return m_task.Id; } } public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } } public TaskStatus Status { get { return m_task.Status; } } @@ -6519,7 +6522,7 @@ namespace System.Threading.Tasks private readonly bool _lookForOce; public UnwrapPromise(Task outerTask, bool lookForOce) - : base((object)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent) + : base((object?)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent) { Debug.Assert(outerTask != null, "Expected non-null outerTask"); _lookForOce = lookForOce; @@ -6594,7 +6597,7 @@ namespace System.Threading.Tasks ThreadPool.UnsafeQueueUserWorkItem(state => { // InvokeCore(completingTask); - var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state; + var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state!; // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 tuple.Item1.InvokeCore(tuple.Item2); }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask)); } @@ -6648,7 +6651,7 @@ namespace System.Threading.Tasks case TaskStatus.Faulted: var edis = task.GetExceptionDispatchInfos(); ExceptionDispatchInfo oceEdi; - OperationCanceledException oce; + OperationCanceledException? oce; if (lookForOce && edis.Count > 0 && (oceEdi = edis[0]) != null && (oce = oceEdi.SourceException as OperationCanceledException) != null) @@ -6670,7 +6673,7 @@ namespace System.Threading.Tasks if (Task.s_asyncDebuggingEnabled) RemoveFromActiveTasks(this); - result = TrySetResult(taskTResult != null ? taskTResult.Result : default); + result = TrySetResult(taskTResult != null ? taskTResult.Result : default!); // TODO-NULLABLE-GENERIC break; } return result; @@ -6681,7 +6684,7 @@ namespace System.Threading.Tasks /// transferring the appropriate results to ourself. /// </summary> /// <param name="task">The inner task returned by the task provided by the user.</param> - private void ProcessInnerTask(Task task) + private void ProcessInnerTask(Task? task) { // If the inner task is null, the proxy should be canceled. if (task == null) |