private Task PrepareResourceAsync()

in src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock`2.cs [759:839]


            private Task PrepareResourceAsync(TResource resource, CancellationToken cancellationToken, bool forcePrepareConcurrent = false)
            {
                Requires.NotNull(resource, nameof(resource));
                Assumes.True(Monitor.IsEntered(this.service.SyncObject));

                // We deliberately ignore the cancellation token in the tasks we create and save because the tasks can be shared
                // across requests and we can't have task continuation chains where tasks within the chain get canceled
                // as that can cause premature starting of the next task in the chain.
                bool forConcurrentUse = forcePrepareConcurrent || !this.service.IsWriteLockHeld;
                AsyncReaderWriterResourceLock<TMoniker, TResource>.Helper.ResourceState finalState = forConcurrentUse ? ResourceState.Concurrent : ResourceState.Exclusive;

                Task? preparationTask = null;

                if (!this.resourcePreparationStates.TryGetValue(resource, out ResourcePreparationTaskState? preparationState))
                {
                    Func<object, Task>? preparationDelegate = forConcurrentUse
                        ? this.prepareResourceConcurrentDelegate
                        : this.prepareResourceExclusiveDelegate;

                    // We kick this off on a new task because we're currently holding a private lock
                    // and don't want to execute arbitrary code.
                    // Let's also hide the ARWL from the delegate if this is a shared lock request.
                    using (forConcurrentUse ? this.service.HideLocks() : default(Suppression))
                    {
                        // We can't currently use the caller's cancellation token for this task because
                        // this task may be shared with others or call this method later, and we wouldn't
                        // want their requests to be cancelled as a result of this first caller cancelling.
                        (preparationState, preparationTask) = ResourcePreparationTaskState.Create(
                            combinedCancellationToken => Task.Factory.StartNew(
                                NullableHelpers.AsNullableArgFunc(preparationDelegate),
                                forConcurrentUse ? Tuple.Create(resource, combinedCancellationToken) : Tuple.Create(resource, this.service.GetAggregateLockFlags(), combinedCancellationToken),
                                combinedCancellationToken,
                                TaskCreationOptions.None,
                                TaskScheduler.Default).Unwrap(),
                            finalState,
                            cancellationToken);
                    }
                }
                else
                {
                    Func<Task, object, Task>? preparationDelegate = null;
                    if (preparationState.State != finalState || preparationState.InnerTask.IsFaulted)
                    {
                        preparationDelegate = forConcurrentUse
                            ? this.prepareResourceConcurrentContinuationDelegate
                            : this.prepareResourceExclusiveContinuationDelegate;
                    }
                    else if (!preparationState.TryJoinPrepationTask(out preparationTask, cancellationToken))
                    {
                        preparationDelegate = forConcurrentUse
                            ? this.prepareResourceConcurrentContinuationOnPossibleCancelledTaskDelegate
                            : this.prepareResourceExclusiveContinuationOnPossibleCancelledTaskDelegateDelegate;
                    }

                    if (preparationTask is null)
                    {
                        Assumes.NotNull(preparationDelegate);

                        // We kick this off on a new task because we're currently holding a private lock
                        // and don't want to execute arbitrary code.
                        // Let's also hide the ARWL from the delegate if this is a shared lock request.
                        using (forConcurrentUse ? this.service.HideLocks() : default(Suppression))
                        {
                            (preparationState, preparationTask) = ResourcePreparationTaskState.Create(
                                combinedCancellationToken => preparationState.InnerTask.ContinueWith(
                                    preparationDelegate!,
                                    forConcurrentUse ? Tuple.Create(resource, combinedCancellationToken) : Tuple.Create(resource, this.service.GetAggregateLockFlags(), combinedCancellationToken),
                                    CancellationToken.None,
                                    TaskContinuationOptions.RunContinuationsAsynchronously,
                                    TaskScheduler.Default).Unwrap(),
                                finalState,
                                cancellationToken);
                        }
                    }
                }

                Assumes.NotNull(preparationState);
                this.resourcePreparationStates[resource] = preparationState;

                return preparationTask;
            }