File: System\Reflection\Metadata\Internal\VirtualHeap.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Reflection.Internal;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
 
namespace System.Reflection.Metadata.Ecma335
{
    // Container for virtual heap blobs that unpins handles on finalization.
    // This is not handled via dispose because the only resource is managed memory
    // and we don't have user visible disposable object that could own this memory.
    //
    // Since the number of virtual blobs we need is small (the number of attribute classes in .winmd files)
    // we can create a pinned handle for each of them.
    // If we needed many more blobs we could create and pin a single byte[] and allocate blobs there.
    internal sealed class VirtualHeap : CriticalDisposableObject
    {
        private struct PinnedBlob
        {
            // can't be read-only since GCHandle is a mutable struct
            public GCHandle Handle;
 
            public readonly int Length;
 
            public PinnedBlob(GCHandle handle, int length)
            {
                Handle = handle;
                Length = length;
            }
 
            public unsafe MemoryBlock GetMemoryBlock() =>
                new MemoryBlock((byte*)Handle.AddrOfPinnedObject(), Length);
        }
 
        // maps raw value of StringHandle or BlobHandle to the corresponding pinned array
        private Dictionary<uint, PinnedBlob>? _blobs;
 
        private VirtualHeap()
        {
            _blobs = new Dictionary<uint, PinnedBlob>();
        }
 
        protected override void Release()
        {
#if FEATURE_CER
            // Make sure the current thread isn't aborted in the middle of the operation.
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            { /* intentionally left blank */ }
            finally
#endif
            {
                var blobs = Interlocked.Exchange(ref _blobs, null);
 
                if (blobs != null)
                {
                    foreach (var blobPair in blobs)
                    {
                        blobPair.Value.Handle.Free();
                    }
                }
            }
        }
 
        private Dictionary<uint, PinnedBlob> GetBlobs()
        {
            var blobs = _blobs;
            if (blobs == null)
            {
                throw new ObjectDisposedException(nameof(VirtualHeap));
            }
 
            return blobs;
        }
 
        public bool TryGetMemoryBlock(uint rawHandle, out MemoryBlock block)
        {
            if (!GetBlobs().TryGetValue(rawHandle, out var blob))
            {
                block = default(MemoryBlock);
                return false;
            }
 
            block = blob.GetMemoryBlock();
            return true;
        }
 
        internal MemoryBlock AddBlob(uint rawHandle, byte[] value)
        {
            var blobs = GetBlobs();
 
            MemoryBlock result;
#if FEATURE_CER
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            { /* intentionally left blank */ }
            finally
#endif
            {
                var blob = new PinnedBlob(GCHandle.Alloc(value, GCHandleType.Pinned), value.Length);
                blobs.Add(rawHandle, blob);
                result = blob.GetMemoryBlock();
            }
 
            return result;
        }
 
        internal static VirtualHeap GetOrCreateVirtualHeap(ref VirtualHeap? lazyHeap)
        {
            if (lazyHeap == null)
            {
                Interlocked.CompareExchange(ref lazyHeap, new VirtualHeap(), null);
            }
 
            return lazyHeap;
        }
    }
}