Skip to content

Commit a410ed4

Browse files
authored
Use Pinned Object Heap for MemoryPool (#21614)
1 parent b062d1b commit a410ed4

File tree

3 files changed

+13
-49
lines changed

3 files changed

+13
-49
lines changed

src/Shared/Buffers.MemoryPool/MemoryPoolBlock.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal MemoryPoolBlock(SlabMemoryPool pool, MemoryPoolSlab slab, int offset, i
2525
Pool = pool;
2626
Slab = slab;
2727

28-
Memory = MemoryMarshal.CreateFromPinnedArray(slab.Array, _offset, _length);
28+
Memory = MemoryMarshal.CreateFromPinnedArray(slab.PinnedArray, _offset, _length);
2929
}
3030

3131
/// <summary>
Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using System.Runtime.InteropServices;
5-
64
namespace System.Buffers
75
{
86
/// <summary>
@@ -11,70 +9,34 @@ namespace System.Buffers
119
/// </summary>
1210
internal class MemoryPoolSlab : IDisposable
1311
{
14-
/// <summary>
15-
/// This handle pins the managed array in memory until the slab is disposed. This prevents it from being
16-
/// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls.
17-
/// </summary>
18-
private GCHandle _gcHandle;
19-
private bool _isDisposed;
20-
21-
public MemoryPoolSlab(byte[] data)
12+
private MemoryPoolSlab(byte[] pinnedData)
2213
{
23-
Array = data;
24-
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
25-
NativePointer = _gcHandle.AddrOfPinnedObject();
14+
PinnedArray = pinnedData;
2615
}
2716

2817
/// <summary>
2918
/// True as long as the blocks from this slab are to be considered returnable to the pool. In order to shrink the
3019
/// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the
3120
/// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will
3221
/// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage
33-
/// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will
34-
/// be unpinned by the slab's Dispose.
22+
/// collected and the slab is no longer references the slab will be garbage collected
3523
/// </summary>
36-
public bool IsActive => !_isDisposed;
24+
public bool IsActive => PinnedArray != null;
3725

38-
public IntPtr NativePointer { get; private set; }
39-
40-
public byte[] Array { get; private set; }
26+
public byte[] PinnedArray { get; private set; }
4127

4228
public static MemoryPoolSlab Create(int length)
4329
{
44-
// allocate and pin requested memory length
45-
var array = new byte[length];
30+
// allocate requested memory length from the pinned memory heap
31+
var pinnedArray = GC.AllocateUninitializedArray<byte>(length, pinned: true);
4632

4733
// allocate and return slab tracking object
48-
return new MemoryPoolSlab(array);
49-
}
50-
51-
protected void Dispose(bool disposing)
52-
{
53-
if (_isDisposed)
54-
{
55-
return;
56-
}
57-
58-
_isDisposed = true;
59-
60-
Array = null;
61-
NativePointer = IntPtr.Zero;
62-
63-
if (_gcHandle.IsAllocated)
64-
{
65-
_gcHandle.Free();
66-
}
67-
}
68-
69-
~MemoryPoolSlab()
70-
{
71-
Dispose(false);
34+
return new MemoryPoolSlab(pinnedArray);
7235
}
7336

7437
public void Dispose()
7538
{
76-
Dispose(true);
77-
GC.SuppressFinalize(this);
39+
PinnedArray = null;
7840
}
7941
}
8042
}

src/Shared/Buffers.MemoryPool/SlabMemoryPool.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Concurrent;
55
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
67
using System.Threading;
78

89
namespace System.Buffers
@@ -110,7 +111,8 @@ private MemoryPoolBlock AllocateSlab()
110111
var slab = MemoryPoolSlab.Create(_slabLength);
111112
_slabs.Push(slab);
112113

113-
var basePtr = slab.NativePointer;
114+
// Get the address for alignment
115+
IntPtr basePtr = Marshal.UnsafeAddrOfPinnedArrayElement(slab.PinnedArray, 0);
114116
// Page align the blocks
115117
var offset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr);
116118
// Ensure page aligned

0 commit comments

Comments
 (0)