summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/SafeHeapHandle.cs
blob: b0c422d0c0849da4485aa9b79c8bdf664c6625c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Runtime.InteropServices
{
    /// <summary>
    /// Handle for heap memory that allows tracking of capacity and reallocating.
    /// </summary>
    [System.Security.SecurityCritical]
    internal sealed class SafeHeapHandle : SafeBuffer
    {
        /// <summary>
        /// Allocate a buffer of the given size if requested.
        /// </summary>
        /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param>
        /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception>
        public SafeHeapHandle(ulong byteLength) : base(ownsHandle: true)
        {
            Resize(byteLength);
        }

        public override bool IsInvalid
        {
            [System.Security.SecurityCritical]
            get
            { return handle == IntPtr.Zero; }
        }

        /// <summary>
        /// Resize the buffer to the given size if requested.
        /// </summary>
        /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param>
        /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception>
        public void Resize(ulong byteLength)
        {
            if (IsClosed) throw new ObjectDisposedException("SafeHeapHandle");

            ulong originalLength = 0;
            if (handle == IntPtr.Zero)
            {
                handle = Marshal.AllocHGlobal((IntPtr)byteLength);
            }
            else
            {
                originalLength = ByteLength;

                // This may or may not be the same handle, may realloc in place. If the
                // handle changes Windows will deal with the old handle, trying to free it will
                // cause an error.
                handle = Marshal.ReAllocHGlobal(pv: handle, cb: (IntPtr)byteLength);
            }

            if (handle == IntPtr.Zero)
            {
                // Only real plausible answer
                throw new OutOfMemoryException();
            }

            if (byteLength > originalLength)
            {
                // Add pressure
                ulong addedBytes = byteLength - originalLength;
                if (addedBytes > long.MaxValue)
                {
                    GC.AddMemoryPressure(long.MaxValue);
                    GC.AddMemoryPressure((long)(addedBytes - long.MaxValue));
                }
                else
                {
                    GC.AddMemoryPressure((long)addedBytes);
                }
            }
            else
            {
                // Shrank or did nothing, release pressure if needed
                RemoveMemoryPressure(originalLength - byteLength);
            }

            Initialize(byteLength);
        }

        private void RemoveMemoryPressure(ulong removedBytes)
        {
            if (removedBytes == 0) return;

            if (removedBytes > long.MaxValue)
            {
                GC.RemoveMemoryPressure(long.MaxValue);
                GC.RemoveMemoryPressure((long)(removedBytes - long.MaxValue));
            }
            else
            {
                GC.RemoveMemoryPressure((long)removedBytes);
            }
        }

        [System.Security.SecurityCritical]
        protected override bool ReleaseHandle()
        {
            IntPtr handle = this.handle;
            this.handle = IntPtr.Zero;

            if (handle != IntPtr.Zero)
            {
                RemoveMemoryPressure(ByteLength);
                Marshal.FreeHGlobal(handle);
            }

            return true;
        }
    }
}