Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All calls to SourceRepository.GetResource must pass a cancellation token #5631

Merged
merged 2 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build/BannedSymbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ M:Microsoft.VisualStudio.Shell.IAsyncServiceProvider.GetServiceAsync(System.Type
M:Microsoft.VisualStudio.Shell.AsyncPackage.GetServiceAsync(System.Type); Do not retrieve services from AsyncPackage, use the Async Service provider instead, which that class implements. Use the Microsoft.VisualStudio.Shell.ServiceExtensions.GetServiceAsync for services that have UI thread affinity or that you don't know about their affinity, and use NuGet.VisualStudio.ServiceProviderExtensions.GetFreeThreadedServiceAsync<TService, TInterface> for free threaded services.

M:System.IO.Path.GetTempPath(); Use NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp)

M:NuGet.Protocol.Core.Types.SourceRepository.GetResource`1(); Pass a cancellation token
M:NuGet.Protocol.Core.Types.SourceRepository.GetResourceAsync`1(); Pass a cancellation token
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ private void AddDependencies(Dictionary<string, Tuple<PackageReaderBase, Packagi
var findLocalPackagesResource = Repository
.Factory
.GetCoreV3(packagesFolderPath)
.GetResource<FindLocalPackagesResource>();
.GetResource<FindLocalPackagesResource>(CancellationToken.None);

// Collect all packages
IDictionary<PackageIdentity, PackageReference> packageReferences =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public override async Task ExecuteCommandAsync()
foreach (PackageSource source in listEndpoints)
{
SourceRepository repository = Repository.Factory.GetCoreV3(source);
PackageSearchResource resource = await repository.GetResourceAsync<PackageSearchResource>();
PackageSearchResource resource = await repository.GetResourceAsync<PackageSearchResource>(cancellationToken);

if (resource is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ private SourceRepository CreateRepositoryFromSource(string source)
{
var packageSource = new PackageSource(source);
var repository = _sourceRepositoryProvider.CreateRepository(packageSource);
var resource = repository.GetResource<PackageSearchResource>();
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved

return repository;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private async Task OrderPackagesForPackagesConfigAsync(

var dependencyInfoResource = await packageManager
.PackagesFolderSourceRepository
.GetResourceAsync<DependencyInfoResource>();
.GetResourceAsync<DependencyInfoResource>(token);

using (var sourceCacheContext = new SourceCacheContext())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ private static async Task<IPackageSearchMetadata[]> GetPackageMetadataThrottledA

foreach (var source in sources)
{
var metadataResource = source.GetResource<PackageMetadataResource>();
var metadataResource = source.GetResource<PackageMetadataResource>(token);
if (metadataResource == null)
{
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ public static async Task<NuGetVersion> GetLatestVersionAsync(PackageSpec origina
{
IList<PackageSource> sources = AddPackageCommandUtility.EvaluateSources(originalPackageSpec.RestoreMetadata.Sources, originalPackageSpec.RestoreMetadata.ConfigFilePaths);

return await AddPackageCommandUtility.GetLatestVersionFromSourcesAsync(sources, logger, packageId, prerelease);
return await AddPackageCommandUtility.GetLatestVersionFromSourcesAsync(sources, logger, packageId, prerelease, CancellationToken.None);
}

private static LibraryDependency GenerateLibraryDependency(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ internal static class AddPackageCommandUtility
/// <param name="prerelease">Whether to include prerelease versions</param>
/// <returns>Return the latest version available from multiple sources and if no version is found returns null.</returns>

public static async Task<NuGetVersion> GetLatestVersionFromSourcesAsync(IList<PackageSource> sources, ILogger logger, string packageId, bool prerelease)
public static async Task<NuGetVersion> GetLatestVersionFromSourcesAsync(IList<PackageSource> sources, ILogger logger, string packageId, bool prerelease, CancellationToken cancellationToken)
{
var maxTasks = Environment.ProcessorCount;
var tasks = new List<Task<NuGetVersion>>();
var latestReleaseList = new List<NuGetVersion>();

foreach (var source in sources)
{
tasks.Add(Task.Run(() => GetLatestVersionFromSourceAsync(source, logger, packageId, prerelease)));
tasks.Add(Task.Run(() => GetLatestVersionFromSourceAsync(source, logger, packageId, prerelease, cancellationToken)));
if (maxTasks <= tasks.Count)
{
var finishedTask = await Task.WhenAny(tasks);
Expand Down Expand Up @@ -64,10 +64,10 @@ public static async Task<NuGetVersion> GetLatestVersionFromSourcesAsync(IList<Pa
/// <param name="packageId">Package to look for</param>
/// <param name="prerelease">Whether to include prerelease versions</param>
/// <returns>Returns the latest version available from a source or a null if non is found.</returns>
public static async Task<NuGetVersion> GetLatestVersionFromSourceAsync(PackageSource source, ILogger logger, string packageId, bool prerelease)
public static async Task<NuGetVersion> GetLatestVersionFromSourceAsync(PackageSource source, ILogger logger, string packageId, bool prerelease, CancellationToken cancellationToken)
{
SourceRepository repository = Repository.Factory.GetCoreV3(source);
PackageMetadataResource resource = await repository.GetResourceAsync<PackageMetadataResource>();
PackageMetadataResource resource = await repository.GetResourceAsync<PackageMetadataResource>(cancellationToken);

using (var cache = new SourceCacheContext())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.Configuration;
Expand Down Expand Up @@ -34,7 +35,7 @@ public static async Task Run(
{
logger.LogWarning(string.Format(CultureInfo.CurrentCulture, Strings.Warning_HttpServerUsage, "delete", packageSource.Source));
}
var packageUpdateResource = await CommandRunnerUtility.GetPackageUpdateResource(sourceProvider, packageSource);
var packageUpdateResource = await CommandRunnerUtility.GetPackageUpdateResource(sourceProvider, packageSource, CancellationToken.None);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently delete is only available in CLI tools, and we don't handle ctrl-c in any of them, so technically the lack of cancellation passthough won't affect NuGet tooling, but if customers are using NuGet.Commands in their own apps, there's no way for them to cancel this request.


await packageUpdateResource.Delete(
packageId,
Expand Down
5 changes: 3 additions & 2 deletions src/NuGet.Core/NuGet.Commands/CommandRunners/PushRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.Configuration;
Expand Down Expand Up @@ -39,7 +40,7 @@ public static async Task Run(
timeoutSeconds = 5 * 60;
}
PackageSource packageSource = CommandRunnerUtility.GetOrCreatePackageSource(sourceProvider, source);
var packageUpdateResource = await CommandRunnerUtility.GetPackageUpdateResource(sourceProvider, packageSource);
var packageUpdateResource = await CommandRunnerUtility.GetPackageUpdateResource(sourceProvider, packageSource, CancellationToken.None);

// Only warn for V3 style sources because they have a service index which is different from the final push url.
if (packageSource.IsHttp && !packageSource.IsHttps && !packageSource.AllowInsecureConnections &&
Expand All @@ -58,7 +59,7 @@ public static async Task Run(
&& !sourceUri.IsFile
&& sourceUri.IsAbsoluteUri)
{
symbolPackageUpdateResource = await CommandRunnerUtility.GetSymbolPackageUpdateResource(sourceProvider, source);
symbolPackageUpdateResource = await CommandRunnerUtility.GetSymbolPackageUpdateResource(sourceProvider, source, CancellationToken.None);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to delete, currently push is only available though NuGet's CLI tools, where we don't handle ctrl-c ourselves, so the .NET runtime will kill the process immediately. However, customers calling this method in their own apps won't be able to cancel the push.

if (symbolPackageUpdateResource != null)
{
symbolSource = symbolPackageUpdateResource.SourceUri.AbsoluteUri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private async Task<LibraryIdentity> FindLibraryCoreAsync(
ILogger logger,
CancellationToken cancellationToken)
{
await EnsureResource();
await EnsureResource(cancellationToken);

if (libraryRange.VersionRange?.MinVersion != null && libraryRange.VersionRange.IsMinInclusive && !libraryRange.VersionRange.IsFloating)
{
Expand Down Expand Up @@ -400,7 +400,7 @@ private async Task<LibraryDependencyInfo> GetDependenciesCoreAsync(
FindPackageByIdDependencyInfo packageInfo = null;
try
{
await EnsureResource();
await EnsureResource(cancellationToken);

if (_throttle != null)
{
Expand Down Expand Up @@ -494,7 +494,7 @@ public async Task<IPackageDownloader> GetPackageDownloaderAsync(

try
{
await EnsureResource();
await EnsureResource(cancellationToken);

if (_throttle != null)
{
Expand Down Expand Up @@ -604,11 +604,11 @@ private static NuGetFramework DeconstructFallbackFrameworks(NuGetFramework nuGet
return nuGetFramework;
}

private async Task EnsureResource()
private async Task EnsureResource(CancellationToken cancellationToken)
{
if (_findPackagesByIdResource == null)
{
var resource = await _sourceRepository.GetResourceAsync<FindPackageByIdResource>();
var resource = await _sourceRepository.GetResourceAsync<FindPackageByIdResource>(cancellationToken);

lock (_lock)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.Configuration;
Expand Down Expand Up @@ -68,12 +69,12 @@ public static string GetApiKey(ISettings settings, string endpoint, string sourc
return apiKey;
}

public static async Task<PackageUpdateResource> GetPackageUpdateResource(IPackageSourceProvider sourceProvider, PackageSource packageSource)
public static async Task<PackageUpdateResource> GetPackageUpdateResource(IPackageSourceProvider sourceProvider, PackageSource packageSource, CancellationToken cancellationToken)
{
var sourceRepositoryProvider = new CachingSourceProvider(sourceProvider);
var sourceRepository = sourceRepositoryProvider.CreateRepository(packageSource);

return await sourceRepository.GetResourceAsync<PackageUpdateResource>();
return await sourceRepository.GetResourceAsync<PackageUpdateResource>(cancellationToken);
}

public static PackageSource GetOrCreatePackageSource(IPackageSourceProvider sourceProvider, string source)
Expand All @@ -97,7 +98,7 @@ public static PackageSource GetOrCreatePackageSource(IPackageSourceProvider sour
return packageSource;
}

public static async Task<SymbolPackageUpdateResourceV3> GetSymbolPackageUpdateResource(IPackageSourceProvider sourceProvider, string source)
public static async Task<SymbolPackageUpdateResourceV3> GetSymbolPackageUpdateResource(IPackageSourceProvider sourceProvider, string source, CancellationToken cancellationToken)
{
// Use a loaded PackageSource if possible since it contains credential info
var packageSource = sourceProvider.LoadPackageSources()
Expand All @@ -112,7 +113,7 @@ public static async Task<SymbolPackageUpdateResourceV3> GetSymbolPackageUpdateRe
var sourceRepositoryProvider = new CachingSourceProvider(sourceProvider);
var sourceRepository = sourceRepositoryProvider.CreateRepository(packageSource);

return await sourceRepository.GetResourceAsync<SymbolPackageUpdateResourceV3>();
return await sourceRepository.GetResourceAsync<SymbolPackageUpdateResourceV3>(cancellationToken);
}
}
}
26 changes: 16 additions & 10 deletions src/NuGet.Core/NuGet.PackageManagement/NuGetPackageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ public async Task<IEnumerable<PackageDependencyInfo>> GetInstalledPackagesDepend
{
var targetFramework = nuGetProject.GetMetadata<NuGetFramework>(NuGetProjectMetadataKeys.TargetFramework);
var installedPackageIdentities = (await nuGetProject.GetInstalledPackagesAsync(token)).Select(pr => pr.PackageIdentity);
return await GetDependencyInfoFromPackagesFolderAsync(installedPackageIdentities, targetFramework, includeUnresolved);
return await GetDependencyInfoFromPackagesFolderAsync(installedPackageIdentities, targetFramework, includeUnresolved, token);
}
else
{
Expand All @@ -1371,7 +1371,9 @@ public async Task<IEnumerable<PackageIdentity>> GetInstalledPackagesInDependency
var installedPackages = await nuGetProject.GetInstalledPackagesAsync(token);
var installedPackageIdentities = installedPackages.Select(pr => pr.PackageIdentity);
var dependencyInfoFromPackagesFolder = await GetDependencyInfoFromPackagesFolderAsync(installedPackageIdentities,
targetFramework);
targetFramework,
includeUnresolved: false,
token);

// dependencyInfoFromPackagesFolder can be null when NuGetProtocolException is thrown
var resolverPackages = dependencyInfoFromPackagesFolder?.Select(package =>
Expand Down Expand Up @@ -2022,7 +2024,7 @@ await PreviewBuildIntegratedProjectActionsAsync(buildIntegratedProject, actions,
else
{
var logger = new ProjectContextLogger(nuGetProjectContext);
var sourceRepository = await GetSourceRepository(packageIdentity, effectiveSources, resolutionContext.SourceCacheContext, logger);
var sourceRepository = await GetSourceRepository(packageIdentity, effectiveSources, resolutionContext.SourceCacheContext, logger, token);
nuGetProjectActions.Add(NuGetProjectAction.CreateInstallProjectAction(packageIdentity, sourceRepository, nuGetProject));
}

Expand All @@ -2049,7 +2051,8 @@ await PreviewBuildIntegratedProjectActionsAsync(buildIntegratedProject, actions,
private static async Task<SourceRepository> GetSourceRepository(PackageIdentity packageIdentity,
IEnumerable<SourceRepository> sourceRepositories,
SourceCacheContext sourceCacheContext,
ILogger logger)
ILogger logger,
CancellationToken cancellationToken)
{
SourceRepository source = null;

Expand All @@ -2063,7 +2066,7 @@ private static async Task<SourceRepository> GetSourceRepository(PackageIdentity
foreach (var sourceRepository in sourceRepositories)
{
// TODO: fetch the resource in parallel also
var metadataResource = await sourceRepository.GetResourceAsync<MetadataResource>();
var metadataResource = await sourceRepository.GetResourceAsync<MetadataResource>(cancellationToken);
if (metadataResource != null)
{
var task = Task.Run(() => metadataResource.Exists(packageIdentity, sourceCacheContext, logger, tokenSource.Token), tokenSource.Token);
Expand Down Expand Up @@ -2376,7 +2379,9 @@ await PreviewBuildIntegratedProjectActionsAsync(buildIntegratedProject, actions,
var log = new LoggerAdapter(nuGetProjectContext);
var installedPackageIdentities = (await nuGetProject.GetInstalledPackagesAsync(token)).Select(pr => pr.PackageIdentity);
var dependencyInfoFromPackagesFolder = await GetDependencyInfoFromPackagesFolderAsync(installedPackageIdentities,
packageReferenceTargetFramework);
packageReferenceTargetFramework,
includeUnresolved: false,
token);

nuGetProjectContext.Log(ProjectManagement.MessageLevel.Info, Strings.ResolvingActionsToUninstallPackage, packageIdentity);
// Step-2 : Determine if the package can be uninstalled based on the metadata resources
Expand All @@ -2392,10 +2397,11 @@ await PreviewBuildIntegratedProjectActionsAsync(buildIntegratedProject, actions,

private async Task<IEnumerable<PackageDependencyInfo>> GetDependencyInfoFromPackagesFolderAsync(IEnumerable<PackageIdentity> packageIdentities,
NuGetFramework nuGetFramework,
bool includeUnresolved = false)
bool includeUnresolved,
CancellationToken cancellationToken)
{
var dependencyInfoResource = await PackagesFolderSourceRepository.GetResourceAsync<DependencyInfoResource>();
return await PackageGraphAnalysisUtilities.GetDependencyInfoForPackageIdentitiesAsync(packageIdentities, nuGetFramework, dependencyInfoResource, NullSourceCacheContext.Instance, includeUnresolved, NullLogger.Instance, CancellationToken.None);
var dependencyInfoResource = await PackagesFolderSourceRepository.GetResourceAsync<DependencyInfoResource>(cancellationToken);
return await PackageGraphAnalysisUtilities.GetDependencyInfoForPackageIdentitiesAsync(packageIdentities, nuGetFramework, dependencyInfoResource, NullSourceCacheContext.Instance, includeUnresolved, NullLogger.Instance, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -3886,7 +3892,7 @@ private static async Task<ResolvedPackage> GetLatestVersionCoreAsync(
Common.ILogger log,
CancellationToken token)
{
var dependencyInfoResource = await source.GetResourceAsync<DependencyInfoResource>();
var dependencyInfoResource = await source.GetResourceAsync<DependencyInfoResource>(token);

// Resolve the package for the project framework and cache the results in the
// resolution context for the gather to use during the next step.
Expand Down
2 changes: 1 addition & 1 deletion src/NuGet.Core/NuGet.Protocol/HttpSource/HttpSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public static HttpSource Create(SourceRepository source, IThrottle throttle)
throw new ArgumentNullException(nameof(throttle));
}

Func<Task<HttpHandlerResource>> factory = () => source.GetResourceAsync<HttpHandlerResource>();
Func<Task<HttpHandlerResource>> factory = () => source.GetResourceAsync<HttpHandlerResource>(CancellationToken.None);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is in a Client SDK package, anything that wants to make an HTTP request has to get the HttpHandlerResource, hence we can be confident that the HttpHandlerResourceProvider isn't going to be making HTTP requests (or do any other async work), so using CancellationToken.None here won't cause any blocking on cancellation requested.


return new HttpSource(source.PackageSource, factory, throttle);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public override async Task<Tuple<bool, INuGetResource>> TryCreate(SourceReposito

if (await source.GetResourceAsync<ServiceIndexResourceV3>(token) != null)
{
var regResource = await source.GetResourceAsync<RegistrationResourceV3>();
var reportAbuseResource = await source.GetResourceAsync<ReportAbuseResourceV3>();
var packageDetailsUriResource = await source.GetResourceAsync<PackageDetailsUriResourceV3>();
var regResource = await source.GetResourceAsync<RegistrationResourceV3>(token);
var reportAbuseResource = await source.GetResourceAsync<ReportAbuseResourceV3>(token);
var packageDetailsUriResource = await source.GetResourceAsync<PackageDetailsUriResourceV3>(token);

var httpSourceResource = await source.GetResourceAsync<HttpSourceResource>(token);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public RawSearchResourceV3Provider()
public override async Task<Tuple<bool, INuGetResource>> TryCreate(SourceRepository source, CancellationToken token)
{
RawSearchResourceV3 curResource = null;
var serviceIndex = await source.GetResourceAsync<ServiceIndexResourceV3>();
var serviceIndex = await source.GetResourceAsync<ServiceIndexResourceV3>(token);

if (serviceIndex != null)
{
Expand Down
Loading