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;
}