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

Stop using CPS JTF #4288

Merged
merged 2 commits into from
Oct 1, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public sealed class NuGetProjectFactory
private readonly IVsProjectThreadingService _threadingService;
private readonly Common.ILogger _logger;

// Reason it's lazy<object> is because we don't want to load any CPS assemblies until
// we're really going to use any of CPS api. Which is why we also don't use nameof or typeof apis.
[Import("Microsoft.VisualStudio.ProjectSystem.IProjectServiceAccessor")]
private Lazy<object> ProjectServiceAccessor { get; set; }

[ImportingConstructor]
public NuGetProjectFactory(
[ImportMany(typeof(INuGetProjectProvider))]
Expand Down Expand Up @@ -69,13 +64,6 @@ public async Task<NuGetProject> TryCreateNuGetProjectAsync(

await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync();

if (vsProjectAdapter.VsHierarchy != null &&
Copy link
Member

Choose a reason for hiding this comment

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

This is very satisfying! :)
It registers on every damn initialization UI delay.

VsHierarchyUtility.IsCPSCapabilityCompliant(vsProjectAdapter.VsHierarchy))
{
// Lazy load the CPS enabled JoinableTaskFactory for the UI.
NuGetUIThreadHelper.SetJoinableTaskFactoryFromService(ProjectServiceAccessor.Value as IProjectServiceAccessor);
}

var exceptions = new List<Exception>();
foreach (var provider in _providers)
{
Expand Down Expand Up @@ -129,13 +117,6 @@ public async Task<TProject> CreateNuGetProjectAsync<TProject>(
return null;
}

if (vsProjectAdapter.VsHierarchy != null &&
VsHierarchyUtility.IsCPSCapabilityCompliant(vsProjectAdapter.VsHierarchy))
{
// Lazy load the CPS enabled JoinableTaskFactory for the UI.
NuGetUIThreadHelper.SetJoinableTaskFactoryFromService(ProjectServiceAccessor.Value as IProjectServiceAccessor);
}

try
{
var nuGetProject = await provider.TryCreateNuGetProjectAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,6 @@ public static JoinableTaskFactory JoinableTaskFactory
}
}

/// <summary>
/// Retrieve the CPS enabled JoinableTaskFactory for the current version of Visual Studio.
/// This overrides the default VsTaskLibraryHelper.ServiceInstance JTF.
/// </summary>
public static void SetJoinableTaskFactoryFromService(IProjectServiceAccessor projectServiceAccessor)
{
if (projectServiceAccessor == null)
{
throw new ArgumentNullException(nameof(projectServiceAccessor));
}

if (LazyJoinableTaskFactory == null)
{
LazyJoinableTaskFactory = new Lazy<JoinableTaskFactory>(() =>
{
// Use IProjectService for Visual Studio 2017
var projectService = projectServiceAccessor.GetProjectService();
return projectService.Services.ThreadingPolicy.JoinableTaskFactory;
},
// This option helps avoiding deadlocks caused by CPS trying to create ProjectServiceHost
// PublicationOnly mode lets parallel threads execute value factory method without
// being blocked on each other.
// It is correct behavior in this case as the value factory provides the same value
// each time it is called and Lazy is used just for caching the value for perf reasons.
LazyThreadSafetyMode.PublicationOnly);
}
}

public static void SetCustomJoinableTaskFactory(JoinableTaskFactory joinableTaskFactory)
{
Assumes.Present(joinableTaskFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,8 @@ public class VsPackageInstaller : IVsPackageInstaller2
private readonly ISettings _settings;
private readonly IVsSolutionManager _solutionManager;
private readonly IDeleteOnRestartManager _deleteOnRestartManager;
private bool _isCPSJTFLoaded;
private readonly INuGetTelemetryProvider _telemetryProvider;

// Reason it's lazy<object> is because we don't want to load any CPS assemblies until
// we're really going to use any of CPS api. Which is why we also don't use nameof or typeof apis.
[Import("Microsoft.VisualStudio.ProjectSystem.IProjectServiceAccessor")]
private Lazy<object> ProjectServiceAccessor { get; set; }

private JoinableTaskFactory PumpingJTF { get; set; }

[ImportingConstructor]
Expand All @@ -60,36 +54,11 @@ public VsPackageInstaller(
_settings = settings;
_solutionManager = solutionManager;
_deleteOnRestartManager = deleteOnRestartManager;
_isCPSJTFLoaded = false;
_telemetryProvider = telemetryProvider;

PumpingJTF = new PumpingJTF(NuGetUIThreadHelper.JoinableTaskFactory);
}

private void RunJTFWithCorrectContext(Project project, Func<Task> asyncTask)
{
if (!_isCPSJTFLoaded)
{
NuGetUIThreadHelper.JoinableTaskFactory.Run(async () =>
{
await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

IVsHierarchy vsHierarchy = await project.ToVsHierarchyAsync();
if (vsHierarchy != null &&
VsHierarchyUtility.IsCPSCapabilityCompliant(vsHierarchy))
{
// Lazy load the CPS enabled JoinableTaskFactory for the UI.
NuGetUIThreadHelper.SetJoinableTaskFactoryFromService(ProjectServiceAccessor.Value as IProjectServiceAccessor);

PumpingJTF = new PumpingJTF(NuGetUIThreadHelper.JoinableTaskFactory);
_isCPSJTFLoaded = true;
}
});
}

PumpingJTF.Run(asyncTask);
}

public void InstallLatestPackage(
string source,
Project project,
Expand All @@ -99,7 +68,7 @@ public void InstallLatestPackage(
{
try
{
RunJTFWithCorrectContext(project, () => InstallPackageAsync(
PumpingJTF.Run(() => InstallPackageAsync(
source,
project,
packageId,
Expand All @@ -125,7 +94,7 @@ public void InstallPackage(string source, Project project, string packageId, Ver
semVer = new NuGetVersion(version);
}

RunJTFWithCorrectContext(project, () => InstallPackageAsync(
PumpingJTF.Run(() => InstallPackageAsync(
source,
project,
packageId,
Expand All @@ -151,7 +120,7 @@ public void InstallPackage(string source, Project project, string packageId, str
_ = NuGetVersion.TryParse(version, out semVer);
}

RunJTFWithCorrectContext(project, () => InstallPackageAsync(
PumpingJTF.Run(() => InstallPackageAsync(
source,
project,
packageId,
Expand Down Expand Up @@ -224,7 +193,7 @@ public void InstallPackagesFromRegistryRepository(string keyName, bool isPreUnzi

try
{
RunJTFWithCorrectContext(project, async () =>
PumpingJTF.Run(async () =>
{
// HACK !!! : This is a hack for PCL projects which send isPreUnzipped = true, but their package source
// (located at C:\Program Files (x86)\Microsoft SDKs\NuGetPackages) follows the V3
Expand Down Expand Up @@ -300,7 +269,7 @@ public void InstallPackagesFromVSExtensionRepository(string extensionId, bool is

try
{
RunJTFWithCorrectContext(project, () =>
PumpingJTF.Run(() =>
{
var repoProvider = new PreinstalledRepositoryProvider(ErrorHandler, _sourceRepositoryProvider);
repoProvider.AddFromExtension(_sourceRepositoryProvider, extensionId);
Expand Down