aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/Memory/ReservedRegion.cs
blob: 3870d4c842c5f2ac735718bbaf96c6895dd207f8 (plain) (blame)
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
using System;

namespace ARMeilleure.Memory
{
    public class ReservedRegion
    {
        public const int DefaultGranularity = 65536; // Mapping granularity in Windows.

        public IJitMemoryBlock Block { get; }

        public IntPtr Pointer => Block.Pointer;

        private readonly ulong _maxSize;
        private readonly ulong _sizeGranularity;
        private ulong _currentSize;

        public ReservedRegion(IJitMemoryAllocator allocator, ulong maxSize, ulong granularity = 0)
        {
            if (granularity == 0)
            {
                granularity = DefaultGranularity;
            }

            Block = allocator.Reserve(maxSize);
            _maxSize = maxSize;
            _sizeGranularity = granularity;
            _currentSize = 0;
        }

        public void ExpandIfNeeded(ulong desiredSize)
        {
            if (desiredSize > _maxSize)
            {
                throw new OutOfMemoryException();
            }

            if (desiredSize > _currentSize)
            {
                // Lock, and then check again. We only want to commit once.
                lock (this)
                {
                    if (desiredSize >= _currentSize)
                    {
                        ulong overflowBytes = desiredSize - _currentSize;
                        ulong moreToCommit = (((_sizeGranularity - 1) + overflowBytes) / _sizeGranularity) * _sizeGranularity; // Round up.
                        Block.Commit(_currentSize, moreToCommit);
                        _currentSize += moreToCommit;
                    }
                }
            }
        }

        public void Dispose()
        {
            Block.Dispose();
        }
    }
}