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

Ensure taggers that compute frozen partial data eventually move to a 'correct' final state. #72878

Merged
merged 36 commits into from
Apr 5, 2024

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Apr 4, 2024

Fixes AB#2016199

Our previous approach for trying to make this work was buggy and was removed in #72384.

The issue there was that we would first compute tags, using frozen-partial-semantics. Then kick off work to get the compilation for the current project (in the host and oop), then kick off an event to compute tags again, again using frozen-partial-semantics. The issue with that was the presumption that if a compilation was successfully retrieved that the next frozen-partial snapshot would contain information from that. That's not how frozen-partial works though. As it is just a snapshot system, once we build that snapshot, it's fixed (and we want that, so that all the features talking to the same frozen snapshot get all the normal caching we want for solution snapshots). So the next frozen-partial-request could still just get the same results as the prior time, effectively making the call pointless.

The new approach changes things fundamentally at the tagger level in the following fashion:

  1. Taggers can state that they support computing results with frozen-partial-semantics.
  2. For taggers that do (classification, reference highlighting, and inheritance margin tagger), the tagger system itself will first compute tags on a frozen-partial solution, calling into the tag provider, also telling it to compute with frozen-partial semantics. It will then report these tags as normal.
  3. Then, after computing that set of possibly-incorrect tags, the tagger will re-enqueue work to compute tags again, this time with non-frozen-partial snapshots. This work is only then done at the next tagging step if no more work has come in to do normal frozen-partial-tagging.
  4. the tagger will then run again, but this time with a normal snapshot, with the normal costs associated with it.

This change now means that the last piece of work one of these taggers will always do is a request to tag on a real full snapshot, with full semantics. So if there are any inaccurate/incomplete tags from a prior frozen-tagging pass, they will be updated to now be correct.

Perf should also be fine as we always prefer to do work for the frozen-case first, continuously computing in a fast, but inaccurate, mode as long as events keep coming in. Only once there is a lull in processing will we be able to get to the full mode.

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner April 4, 2024 18:34
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 4, 2024
@CyrusNajmabadi CyrusNajmabadi marked this pull request as draft April 4, 2024 18:34
@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review April 4, 2024 19:12
// amount of time waiting for all the compilation information to be built. For example, we can classify types
// that we've parsed in other files, or partially loaded from metadata, even if we're still parsing/loading.
// For cross language projects, this also produces semantic classifications more quickly as we do not have to
// wait on skeletons to be built.
Copy link
Member Author

Choose a reason for hiding this comment

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

this is a move of this comment.

@Pilchie
Copy link
Member

Pilchie commented Apr 4, 2024

✅ Successfully linked to Azure Boards work item(s):

EnqueueWork(highPriority, _dataSource.SupportsFrozenPartialSemantics);
}

private void EnqueueWork(bool highPriority, bool frozenPartialSemantics)
Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell for eyes on thsi threading logic.

}
else
{
// We're asking for expensive tags, and this tagger supports frozen partial tags. Kick off the work
Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell for extra attention here.

}

protected override bool TagEquals(IClassificationTag tag1, IClassificationTag tag2)
=> tag1.ClassificationType.Classification == tag2.ClassificationType.Classification;
public async Task ProduceTagsAsync(
Copy link
Member Author

Choose a reason for hiding this comment

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

these are just method moves from a utilities class far awqay. they were only called here (and referred to tagger types). so i just moved them all back over. they are otherwise unchanged.

/// cancel the expensive non-frozen tagging pass (which might be computing skeletons, SG docs, etc.), do the
/// next cheap frozen-tagging-pass, and then push the expensive-nonfrozen-tagging-pass to the end again.
/// </summary>
private readonly CancellationSeries _nonFrozenComputationCancellationSeries = new();
Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell for eyes.

{
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

i felt like there could be a cleaner way of writing all this. but i couldn't figure it out :)

@@ -60,7 +60,7 @@ public static async Task<int[]> HandleRequestHelperAsync(Document document, Immu
// If the full compilation is not yet available, we'll try getting a partial one. It may contain inaccurate
// results but will speed up how quickly we can respond to the client's request.
document = document.WithFrozenPartialSemantics(cancellationToken);
options = options with { ForceFrozenPartialSemanticsForCrossProcessOperations = true };
options = options with { FrozenPartialSemantics = true };
Copy link
Member

Choose a reason for hiding this comment

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

📝 This seems like something we'll need to follow-up on.

var highPriority = changes.Contains(true);
return new ValueTask<VoidResult>(RecomputeTagsAsync(highPriority, cancellationToken));
var highPriority = changes.Contains(x => x.highPriority);
var frozenPartialSemantics = changes.Contains(t => t.frozenPartialSemantics);
Copy link
Member

Choose a reason for hiding this comment

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

);

if we have one request of (highPriority: true, frozenPartialSemantics: false) and another (highPriority: false, frozenPartialSemantics: true), then we'd recompute with a mix setting of (true, true), is that intended? If so, pls add comment to clearify that. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

will add comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants