@@ -251,6 +251,17 @@ private sealed class SkeletonReferenceSet
251251 /// </summary>
252252 private readonly DeferredDocumentationProvider _documentationProvider ;
253253
254+ /// <summary>
255+ /// The actual assembly metadata produced from the data pointed to in <see cref="_storage"/>.
256+ /// </summary>
257+ private readonly AsyncLazy < AssemblyMetadata ? > _metadata ;
258+
259+ /// <summary>
260+ /// Mapping from different values of <see cref="MetadataReferenceProperties"/> to the actual <see
261+ /// cref="MetadataReference"/> produced for it. Used so that if the same compilation is referenced from
262+ /// different projects with/without the same properties that the right references are handed out (and shared
263+ /// if possible).
264+ /// </summary>
254265 /// <remarks>
255266 /// This instance should be locked when being read/written.
256267 /// </remarks>
@@ -264,58 +275,34 @@ public SkeletonReferenceSet(
264275 _storage = storage ;
265276 _assemblyName = assemblyName ;
266277 _documentationProvider = documentationProvider ;
267- }
268-
269- public MetadataReference ? TryGetAlreadyBuiltMetadataReference ( MetadataReferenceProperties properties )
270- {
271- // lookup first and eagerly return cached value if we have it.
272- lock ( _metadataReferences )
273- return _metadataReferences . TryGetValue ( properties , out var lazy ) && lazy . TryGetValue ( out var result ) ? result : null ;
274- }
275-
276- public Task < MetadataReference ? > GetMetadataReferenceAsync ( MetadataReferenceProperties properties , CancellationToken cancellationToken )
277- {
278- AsyncLazy < MetadataReference ? > ? lazy ;
279- lock ( _metadataReferences )
280- {
281- if ( ! _metadataReferences . TryGetValue ( properties , out lazy ) )
282- {
283- // note: computing the reference is actually synchronous. However, this ensures we don't have N
284- // threads blocking on a lazy to compute the work. Instead, we'll only occupy one thread, while
285- // any concurrent requests asynchronously wait for that work to be one.
286- lazy = new AsyncLazy < MetadataReference ? > ( c => ComputeReferenceAsync ( properties , c ) , cacheResult : true ) ;
287- _metadataReferences . Add ( properties , lazy ) ;
288- }
289- }
290278
291- return lazy . GetValueAsync ( cancellationToken ) ;
292-
293- Task < MetadataReference ? > ComputeReferenceAsync ( MetadataReferenceProperties properties , CancellationToken cancellationToken )
294- {
295- return Task . FromResult ( CreateReference ( properties . Aliases , properties . EmbedInteropTypes , _documentationProvider ) ) ;
296- }
279+ // note: computing the assembly metadata is actually synchronous. However, this ensures we don't have N
280+ // threads blocking on a lazy to compute the work. Instead, we'll only occupy one thread, while any
281+ // concurrent requests asynchronously wait for that work to be one.
282+ _metadata = new AsyncLazy < AssemblyMetadata ? > ( c => Task . FromResult ( ComputeMetadata ( _storage , c ) ) , cacheResult : true ) ;
297283 }
298284
299- private MetadataReference ? CreateReference ( ImmutableArray < string > aliases , bool embedInteropTypes , DocumentationProvider documentationProvider )
285+ private static AssemblyMetadata ? ComputeMetadata ( ITemporaryStreamStorage ? storage , CancellationToken cancellationToken )
300286 {
301- if ( _storage == null )
287+ if ( storage == null )
302288 return null ;
303289
304290 // first see whether we can use native memory directly.
305- var stream = _storage . ReadStream ( ) ;
306- AssemblyMetadata metadata ;
291+ var stream = storage . ReadStream ( cancellationToken ) ;
307292
308293 if ( stream is ISupportDirectMemoryAccess supportNativeMemory )
309294 {
310295 // this is unfortunate that if we give stream, compiler will just re-copy whole content to
311296 // native memory again. this is a way to get around the issue by we getting native memory ourselves and then
312297 // give them pointer to the native memory. also we need to handle lifetime ourselves.
313- metadata = AssemblyMetadata . Create ( ModuleMetadata . CreateFromImage ( supportNativeMemory . GetPointer ( ) , ( int ) stream . Length ) ) ;
298+ var metadata = AssemblyMetadata . Create ( ModuleMetadata . CreateFromImage ( supportNativeMemory . GetPointer ( ) , ( int ) stream . Length ) ) ;
314299
315300 // Tie lifetime of stream to metadata we created. It is important to tie this to the Metadata and not the
316301 // metadata reference, as PE symbols hold onto just the Metadata. We can use Add here since we created
317302 // a brand new object in AssemblyMetadata.Create above.
318303 s_lifetime . Add ( metadata , supportNativeMemory ) ;
304+
305+ return metadata ;
319306 }
320307 else
321308 {
@@ -325,14 +312,43 @@ public SkeletonReferenceSet(
325312
326313 // We don't deterministically release the resulting metadata since we don't know
327314 // when we should. So we leave it up to the GC to collect it and release all the associated resources.
328- metadata = AssemblyMetadata . CreateFromStream ( stream ) ;
315+ return AssemblyMetadata . CreateFromStream ( stream , leaveOpen : false ) ;
316+ }
317+ }
318+
319+ public MetadataReference ? TryGetAlreadyBuiltMetadataReference ( MetadataReferenceProperties properties )
320+ {
321+ // lookup first and eagerly return cached value if we have it.
322+ lock ( _metadataReferences )
323+ return _metadataReferences . TryGetValue ( properties , out var lazy ) && lazy . TryGetValue ( out var result ) ? result : null ;
324+ }
325+
326+ public Task < MetadataReference ? > GetMetadataReferenceAsync ( MetadataReferenceProperties properties , CancellationToken cancellationToken )
327+ {
328+ AsyncLazy < MetadataReference ? > ? lazy ;
329+ lock ( _metadataReferences )
330+ {
331+ if ( ! _metadataReferences . TryGetValue ( properties , out lazy ) )
332+ {
333+ lazy = new AsyncLazy < MetadataReference ? > ( c => ComputeReferenceAsync ( properties , c ) , cacheResult : true ) ;
334+ _metadataReferences . Add ( properties , lazy ) ;
335+ }
329336 }
330337
331- return metadata . GetReference (
332- documentation : documentationProvider ,
333- aliases : aliases ,
334- embedInteropTypes : embedInteropTypes ,
335- display : _assemblyName ) ;
338+ return lazy . GetValueAsync ( cancellationToken ) ;
339+
340+ async Task < MetadataReference ? > ComputeReferenceAsync ( MetadataReferenceProperties properties , CancellationToken cancellationToken )
341+ {
342+ var metadata = await _metadata . GetValueAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
343+ if ( metadata == null )
344+ return null ;
345+
346+ return metadata . GetReference (
347+ documentation : _documentationProvider ,
348+ aliases : properties . Aliases ,
349+ embedInteropTypes : properties . EmbedInteropTypes ,
350+ display : _assemblyName ) ;
351+ }
336352 }
337353 }
338354 }
0 commit comments