Cache the AssemblyMetadata produced for any given compilation#61649
Cache the AssemblyMetadata produced for any given compilation#61649CyrusNajmabadi merged 6 commits intodotnet:mainfrom
Conversation
Add docs Simplify Simplify
src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs
Outdated
Show resolved
Hide resolved
…SkeletonReferenceCache.cs
src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs
Outdated
Show resolved
Hide resolved
…SkeletonReferenceCache.cs
| /// want to keep it's skeleton cache around. | ||
| /// </summary> | ||
| private readonly SemaphoreSlim _emitGate = new(initialCount: 1); | ||
| private static readonly ConditionalWeakTable<Compilation, AsyncLazy<SkeletonReferenceSet?>> s_compilationToSkeletonSet = new(); |
There was a problem hiding this comment.
no need for the gate. the CWT is already thread-safe on access, and the use of AsyncLazy means there's at most one computation ever happening to produce the set.
| var lazy = s_compilationToSkeletonSet.GetValue(compilation, | ||
| compilation => new AsyncLazy<SkeletonReferenceSet?>( | ||
| cancellationToken => Task.FromResult(CreateSkeletonSet(workspace, compilation, cancellationToken)), | ||
| cacheResult: true)); |
There was a problem hiding this comment.
would love these to be static lambdas. However, we are keyed off the compilation, but need to capture 'workspace' to create the result.
I don't anticipate these allocations being a problem. If they are, we can always do the pattern of looking up first, then dispatching to a local function that does the computation if that fails.
| new DeferredDocumentationProvider(compilation)); | ||
| } | ||
|
|
||
| private static (AssemblyMetadata, ISupportDirectMemoryAccess?) ComputeMetadata(ITemporaryStreamStorage storage, CancellationToken cancellationToken) |
There was a problem hiding this comment.
this is a move from our sibling type.
| /// Used to tie lifetime of the underlying direct memory to any metadata reference we pass out. This way the | ||
| /// memory will not be GC'ed while this reference is alive. | ||
| /// </summary> | ||
| private readonly ISupportDirectMemoryAccess? _directMemoryAccess; |
There was a problem hiding this comment.
the reference set type is now very concrete. it holds the real computed data in a strong fashion, and allows it to be found associated both with compilation-trackers (which pass the set forward), or associated with compilations, in case we have compilation-reuse across trackers.
There was a problem hiding this comment.
this type is now exceedingly trivial as well, as all it does it hold onto this raw data and then create/cache using it to create the actual PEReference objects that are handed out.
|
@dibarbet Can you take a look today? Thanks! |
will look this morning! |
This ensures that if we produce an AssemblyMetadata (and skeleton PEReference) to be consumed a downstream project, that we can then reuse that same data for a different downstream project, even if the first downstream project is GC'ed.
As long as the originating compilation is around, then we'll only build a skeleton for it at most once. This also benefits any scenarios that may exist where we might get a forked compilation-tracker but that tracker ends up producing the same compilation (@jasonmalinowski for if this is possible).
This will also get cleaner once #61630 goes in.