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

Always try to use cached data for import completion even if outdated #59760

Merged
merged 22 commits into from
Mar 22, 2022

Conversation

genlu
Copy link
Member

@genlu genlu commented Feb 25, 2022

And refresh cache in background.

genlu added 6 commits February 9, 2022 14:21
Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.
Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.
@genlu genlu requested a review from a team as a code owner February 25, 2022 02:18
var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).SelectAsArray(id => solution.GetRequiredProject(id));
var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).Select(id => solution.GetRequiredProject(id)).Where(p => p.SupportsCompilation);

projectsBuilder.Add(currentProject);
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that we no longer have any special treatment for current project, i.e. we return whatever in the cache, or if the cache isn't ready, we don't return anything from it.

@genlu genlu requested a review from a team February 28, 2022 20:36
@CyrusNajmabadi
Copy link
Member

Checking.

@@ -47,6 +48,9 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC
var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false);
if (!ShouldProvideCompletion(completionContext, syntaxContext))
{
// Trigger a backgound task to warm up cache and return immediately.
// This won't cause multiple cache update to run simultaneously because WarmUpCacheAsync is implemented by AsyncBatchingWorkQueue.
_ = Task.Run(() => WarmUpCacheAsync(document));
Copy link
Member

Choose a reason for hiding this comment

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

You shouldn't need the Task.Run. You shoudl just add an item to the work queue.

return Task.CompletedTask;

_workQueue.AddWork(project.Id);
return _workQueue.WaitUntilCurrentBatchCompletesAsync();
Copy link
Member

Choose a reason for hiding this comment

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

you definitely don't want to wait on the work to complete :) that somewhat defeats the purpose. you just want to put the item in the queue and then know that it will be taken care of later.

Copy link
Member Author

@genlu genlu Mar 1, 2022

Choose a reason for hiding this comment

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

This is the code path that returns the cache update task, not the code path for completion. That said, this can definitely use a little cleanup to make things clearer. Will update

Copy link
Member

Choose a reason for hiding this comment

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

could you point me to the code that looks at the return value of this method?


static bool HasGlobalAlias(MetadataReference? metadataReference)
=> metadataReference != null && (metadataReference.Properties.Aliases.IsEmpty || metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias));
return (resultBuilder.ToImmutable(), isPartialResult);
Copy link
Member

Choose a reason for hiding this comment

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

just curious what you do with isPArtialResult on the consumption side.

}

private static bool HasGlobalAlias(ImmutableArray<string> aliases)
=> aliases.IsEmpty || aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias);
Copy link
Member

Choose a reason for hiding this comment

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

this needs doc'ing. why does 'aliases.IsEmpty' imply you have global aliases?

}
}
Copy link
Member

Choose a reason for hiding this comment

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

does thsi run in oop? or in VS? If in OOP, i'm ok with this running in parallel over all the PERefs. Unless you want it serial? (i can see args both ways).

Copy link
Member Author

@genlu genlu Mar 1, 2022

Choose a reason for hiding this comment

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

This is in OOP (if enabled, that is)

@genlu genlu requested a review from a team as a code owner March 3, 2022 01:52
Copy link
Member

@sharwell sharwell left a comment

Choose a reason for hiding this comment

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

Missing async tracking

{
internal interface IImportCompletionCacheService<TProject, TPortableExecutable> : IWorkspaceService
{
// PE references are keyed on assembly path.
IDictionary<string, TPortableExecutable> PEItemsCache { get; }

IDictionary<ProjectId, TProject> ProjectItemsCache { get; }

CancellationToken DisposalToken { get; }
Copy link
Member Author

Choose a reason for hiding this comment

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

All the change in the last commits are refactoring except this and its implementation.

TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
listener,
CacheService.DisposalToken);
Copy link
Member Author

Choose a reason for hiding this comment

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

Use token for workqueue

TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
listenerProvider.GetListener(FeatureAttribute.CompletionSet),
cacheService.DisposalToken);
Copy link
Member Author

Choose a reason for hiding this comment

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

Use token for workqueue

// This is the implementation at Editor layer to provide a CancellationToken
// for the workqueue used for background cache refresh.
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<ExtensionMethodImportCompletionCacheEntry, object>), ServiceLayer.Editor), Shared]
internal sealed class EditorExtensionMethodImportCompletionCacheServiceFactory
Copy link
Member Author

Choose a reason for hiding this comment

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

new Editor layer factory

{
// This is the implementation at Editor layer to provide a CancellationToken
// for the workqueue used for background cache refresh.
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<TypeImportCompletionCacheEntry, TypeImportCompletionCacheEntry>), ServiceLayer.Editor), Shared]
Copy link
Member Author

Choose a reason for hiding this comment

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

new Editor layer factory

@genlu
Copy link
Member Author

genlu commented Mar 16, 2022

Hey @CyrusNajmabadi, another look at this please?

}
}

public class Builder : IDisposable
Copy link
Member

Choose a reason for hiding this comment

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

do you pass your builders around? you coudl consider these being structs too. if you do pass it around, yuo will need to pass by ref given the mutable publicItemCount.

@genlu genlu enabled auto-merge March 18, 2022 22:17
Comment on lines 48 to 49
var exportProvider = (IMefHostExportProvider)project.Solution.Workspace.Services.HostServices;
var listenerProvider = exportProvider.GetExports<AsynchronousOperationListenerProvider>().Single().Value;
Copy link
Member

@sharwell sharwell Mar 21, 2022

Choose a reason for hiding this comment

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

📝 Code shouldn't assume HostServices implements IMefHostExportProvider. Can we not pass the listener to the constructor of this type?

Copy link
Member Author

Choose a reason for hiding this comment

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

You are commenting on an out-of-date snapshot, this is moved to https://github.com/dotnet/roslyn/pull/59760/files#diff-91071542ca3996ad4d7178bc276d40deaae3e2a3b7522531f29d869e1fb636ccR48

That said, I'm still using the same code to fet listener provider there. I assume I can still import the provider in the constructor safely?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@genlu genlu dismissed sharwell’s stale review March 22, 2022 00:03

Dismissed since original concern (as well as subsequent comments) have been addressed

@genlu genlu merged commit f690017 into dotnet:main Mar 22, 2022
@ghost ghost added this to the Next milestone Mar 22, 2022
@genlu genlu deleted the UseCache branch March 22, 2022 00:03
@allisonchou allisonchou modified the milestones: Next, 17.2.P3 Mar 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants