in src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs [111:192]
public Task<T> GetValueAsync(CancellationToken cancellationToken)
{
if (!((this.value is object && this.value.IsCompleted) || this.recursiveFactoryCheck.Value is null))
{
// PERF: we check the condition and *then* retrieve the string resource only on failure
// because the string retrieval has shown up as significant on ETL traces.
Verify.FailOperation(Strings.ValueFactoryReentrancy);
}
if (this.value is null)
{
if (Monitor.IsEntered(this.syncObject))
{
// PERF: we check the condition and *then* retrieve the string resource only on failure
// because the string retrieval has shown up as significant on ETL traces.
Verify.FailOperation(Strings.ValueFactoryReentrancy);
}
InlineResumable? resumableAwaiter = null;
lock (this.syncObject)
{
// Note that if multiple threads hit GetValueAsync() before
// the valueFactory has completed its synchronous execution,
// then only one thread will execute the valueFactory while the
// other threads synchronously block till the synchronous portion
// has completed.
if (this.value is null)
{
RoslynDebug.Assert(this.valueFactory is object);
cancellationToken.ThrowIfCancellationRequested();
resumableAwaiter = new InlineResumable();
Func<Task<T>>? originalValueFactory = this.valueFactory;
this.valueFactory = null;
Func<Task<T>> valueFactory = async delegate
{
try
{
await resumableAwaiter;
return await originalValueFactory().ConfigureAwaitRunInline();
}
finally
{
this.jobFactory = null;
this.joinableTask = null;
}
};
this.recursiveFactoryCheck.Value = RecursiveCheckSentinel;
try
{
if (this.jobFactory is object)
{
// Wrapping with RunAsync allows a future caller
// to synchronously block the Main thread waiting for the result
// without leading to deadlocks.
this.joinableTask = this.jobFactory.RunAsync(valueFactory);
this.value = this.joinableTask.Task;
}
else
{
this.value = valueFactory();
}
}
finally
{
this.recursiveFactoryCheck.Value = null;
}
}
}
// Allow the original value factory to actually run.
resumableAwaiter?.Resume();
}
if (!this.value.IsCompleted)
{
this.joinableTask?.JoinAsync(cancellationToken).Forget();
}
return this.value.WithCancellation(cancellationToken);
}