-
Notifications
You must be signed in to change notification settings - Fork 761
/
MemoryPoolBlock.cs
136 lines (117 loc) · 4.47 KB
/
MemoryPoolBlock.cs
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.IO.Pipelines;
namespace System.Buffers
{
/// <summary>
/// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The
/// individual blocks are then treated as independent array segments.
/// </summary>
internal class MemoryPoolBlock : OwnedMemory<byte>
{
private readonly int _offset;
private readonly int _length;
private int _referenceCount;
private bool _disposed;
/// <summary>
/// This object cannot be instantiated outside of the static Create method
/// </summary>
protected MemoryPoolBlock(SlabMemoryPool pool, MemoryPoolSlab slab, int offset, int length)
{
_offset = offset;
_length = length;
Pool = pool;
Slab = slab;
}
/// <summary>
/// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool.
/// </summary>
public SlabMemoryPool Pool { get; }
/// <summary>
/// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory.
/// </summary>
public MemoryPoolSlab Slab { get; }
public override int Length => _length;
public override Span<byte> Span
{
get
{
if (IsDisposed) ThrowHelper.ThrowObjectDisposedException(ExceptionArgument.MemoryPoolBlock);
return new Span<byte>(Slab.Array, _offset, _length);
}
}
#if BLOCK_LEASE_TRACKING
public bool IsLeased { get; set; }
public string Leaser { get; set; }
#endif
~MemoryPoolBlock()
{
if (Slab != null && Slab.IsActive)
{
#if DEBUG
Debug.Assert(false, $"{Environment.NewLine}{Environment.NewLine}*** Block being garbage collected instead of returned to pool" +
#if BLOCK_LEASE_TRACKING
$": {Leaser}" +
#endif
$" ***{ Environment.NewLine}");
#endif
// Need to make a new object because this one is being finalized
Pool.Return(new MemoryPoolBlock(Pool, Slab, _offset, _length));
}
}
internal static MemoryPoolBlock Create(
int offset,
int length,
SlabMemoryPool pool,
MemoryPoolSlab slab)
{
return new MemoryPoolBlock(pool, slab, offset, length);
}
protected void OnZeroReferences()
{
Pool.Return(this);
}
protected override void Dispose(bool disposing)
{
_disposed = true;
}
public override void Retain()
{
if (IsDisposed) ThrowHelper.ThrowObjectDisposedException(ExceptionArgument.MemoryPoolBlock);
Interlocked.Increment(ref _referenceCount);
}
public override bool Release()
{
int newRefCount = Interlocked.Decrement(ref _referenceCount);
if (newRefCount < 0) ThrowHelper.ThrowInvalidOperationException_ReferenceCountZero();
if (newRefCount == 0)
{
OnZeroReferences();
return false;
}
return true;
}
protected override bool IsRetained => _referenceCount > 0;
public override bool IsDisposed => _disposed;
// In kestrel both MemoryPoolBlock and OwnedMemory end up in the same assembly so
// this method access modifiers need to be `protected internal`
protected override bool TryGetArray(out ArraySegment<byte> arraySegment)
{
if (IsDisposed) ThrowHelper.ThrowObjectDisposedException(ExceptionArgument.MemoryPoolBlock);
arraySegment = new ArraySegment<byte>(Slab.Array, _offset, _length);
return true;
}
public override MemoryHandle Pin(int byteOffset = 0)
{
Retain(); // checks IsDisposed
if (byteOffset < 0 || byteOffset > _length) ThrowHelper.ThrowArgumentOutOfRangeException(_length, byteOffset);
unsafe
{
return new MemoryHandle(this, (Slab.NativePointer + _offset + byteOffset).ToPointer());
}
}
}
}