Merge branch 'benaadams/memorypool' into dev
This commit is contained in:
commit
f8c2c2470f
|
|
@ -253,7 +253,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
var result = _rawSocketInput.IncomingStart();
|
||||
|
||||
return handle.Libuv.buf_init(
|
||||
result.Pin() + result.End,
|
||||
result.DataArrayPtr + result.End,
|
||||
result.Data.Offset + result.Data.Count - result.End);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -684,14 +684,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
var end = _lockedEnd.Block;
|
||||
if (block == end)
|
||||
{
|
||||
end.Unpin();
|
||||
return;
|
||||
}
|
||||
|
||||
while (block.Next != end)
|
||||
{
|
||||
block = block.Next;
|
||||
block.Unpin();
|
||||
}
|
||||
block.Next = null;
|
||||
|
||||
|
|
@ -705,7 +703,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
var returnBlock = block;
|
||||
block = block.Next;
|
||||
|
||||
returnBlock.Unpin();
|
||||
returnBlock.Pool.Return(returnBlock);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,24 +63,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
/// <summary>
|
||||
/// Called to take a block from the pool.
|
||||
/// </summary>
|
||||
/// <param name="minimumSize">The block returned must be at least this size. It may be larger than this minimum size, and if so,
|
||||
/// the caller may write to the block's entire size rather than being limited to the minumumSize requested.</param>
|
||||
/// <returns>The block that is reserved for the called. It must be passed to Return when it is no longer being used.</returns>
|
||||
public MemoryPoolBlock Lease(int minimumSize = MaxPooledBlockLength)
|
||||
public MemoryPoolBlock Lease()
|
||||
{
|
||||
if (minimumSize > _blockLength)
|
||||
{
|
||||
// The requested minimumSize is actually larger then the usable memory of a single block.
|
||||
// Because this is the degenerate case, a one-time-use byte[] array and tracking object are allocated.
|
||||
// When this block tracking object is returned it is not added to the pool - instead it will be
|
||||
// allowed to be garbage collected normally.
|
||||
return MemoryPoolBlock.Create(
|
||||
new ArraySegment<byte>(new byte[minimumSize]),
|
||||
dataPtr: IntPtr.Zero,
|
||||
pool: this,
|
||||
slab: null);
|
||||
}
|
||||
|
||||
MemoryPoolBlock block;
|
||||
if (_blocks.TryDequeue(out block))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,18 +11,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
/// </summary>
|
||||
public class MemoryPoolBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// If this block represents a one-time-use memory object, this GCHandle will hold that memory object at a fixed address
|
||||
/// so it can be used in native operations.
|
||||
/// </summary>
|
||||
private GCHandle _pinHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Native address of the first byte of this block's Data memory. It is null for one-time-use memory, or copied from
|
||||
/// the Slab's ArrayPtr for a slab-block segment. The byte it points to corresponds to Data.Array[0], and in practice you will always
|
||||
/// use the _dataArrayPtr + Start or _dataArrayPtr + End, which point to the start of "active" bytes, or point to just after the "active" bytes.
|
||||
/// use the DataArrayPtr + Start or DataArrayPtr + End, which point to the start of "active" bytes, or point to just after the "active" bytes.
|
||||
/// </summary>
|
||||
private IntPtr _dataArrayPtr;
|
||||
public readonly IntPtr DataArrayPtr;
|
||||
|
||||
internal unsafe readonly byte* DataFixedPtr;
|
||||
|
||||
/// <summary>
|
||||
/// The array segment describing the range of memory this block is tracking. The caller which has leased this block may only read and
|
||||
|
|
@ -33,8 +29,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
/// <summary>
|
||||
/// This object cannot be instantiated outside of the static Create method
|
||||
/// </summary>
|
||||
protected MemoryPoolBlock()
|
||||
unsafe protected MemoryPoolBlock(IntPtr dataArrayPtr)
|
||||
{
|
||||
DataArrayPtr = dataArrayPtr;
|
||||
DataFixedPtr = (byte*)dataArrayPtr.ToPointer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -76,20 +74,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
|
||||
~MemoryPoolBlock()
|
||||
{
|
||||
Debug.Assert(!_pinHandle.IsAllocated, "Ad-hoc memory block wasn't unpinned");
|
||||
Debug.Assert(Slab == null || !Slab.IsActive, "Block being garbage collected instead of returned to pool");
|
||||
|
||||
if (_pinHandle.IsAllocated)
|
||||
{
|
||||
// if this is a one-time-use block, ensure that the GCHandle does not leak
|
||||
_pinHandle.Free();
|
||||
}
|
||||
|
||||
if (Slab != null && Slab.IsActive)
|
||||
{
|
||||
Pool.Return(new MemoryPoolBlock
|
||||
Pool.Return(new MemoryPoolBlock(DataArrayPtr)
|
||||
{
|
||||
_dataArrayPtr = _dataArrayPtr,
|
||||
Data = Data,
|
||||
Pool = Pool,
|
||||
Slab = Slab,
|
||||
|
|
@ -97,49 +87,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to ensure that a block is pinned, and return the pointer to the native address
|
||||
/// of the first byte of this block's Data memory. Arriving data is read into Pin() + End.
|
||||
/// Outgoing data is read from Pin() + Start.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IntPtr Pin()
|
||||
{
|
||||
Debug.Assert(!_pinHandle.IsAllocated);
|
||||
|
||||
if (_dataArrayPtr != IntPtr.Zero)
|
||||
{
|
||||
// this is a slab managed block - use the native address of the slab which is always locked
|
||||
return _dataArrayPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is one-time-use memory - lock the managed memory until Unpin is called
|
||||
_pinHandle = GCHandle.Alloc(Data.Array, GCHandleType.Pinned);
|
||||
return _pinHandle.AddrOfPinnedObject();
|
||||
}
|
||||
}
|
||||
|
||||
public void Unpin()
|
||||
{
|
||||
if (_dataArrayPtr == IntPtr.Zero)
|
||||
{
|
||||
// this is one-time-use memory - unlock the managed memory
|
||||
Debug.Assert(_pinHandle.IsAllocated);
|
||||
_pinHandle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public static MemoryPoolBlock Create(
|
||||
internal static MemoryPoolBlock Create(
|
||||
ArraySegment<byte> data,
|
||||
IntPtr dataPtr,
|
||||
MemoryPool pool,
|
||||
MemoryPoolSlab slab)
|
||||
{
|
||||
return new MemoryPoolBlock
|
||||
return new MemoryPoolBlock(dataPtr)
|
||||
{
|
||||
Data = data,
|
||||
_dataArrayPtr = dataPtr,
|
||||
Pool = pool,
|
||||
Slab = slab,
|
||||
Start = data.Offset,
|
||||
|
|
|
|||
|
|
@ -175,10 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
}
|
||||
else if (_block.End - _index >= sizeof(long))
|
||||
{
|
||||
fixed (byte* ptr = &_block.Array[_index])
|
||||
{
|
||||
return *(long*)(ptr);
|
||||
}
|
||||
return *(long*)(_block.DataFixedPtr + _index);
|
||||
}
|
||||
else if (_block.Next == null)
|
||||
{
|
||||
|
|
@ -194,17 +191,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
return -1;
|
||||
}
|
||||
|
||||
long blockLong;
|
||||
fixed (byte* ptr = &_block.Array[_block.End - sizeof(long)])
|
||||
{
|
||||
blockLong = *(long*)(ptr);
|
||||
}
|
||||
var blockLong = *(long*)(_block.DataFixedPtr + _block.End - sizeof(long));
|
||||
|
||||
long nextLong;
|
||||
fixed (byte* ptr = &_block.Next.Array[_block.Next.Start])
|
||||
{
|
||||
nextLong = *(long*)(ptr);
|
||||
}
|
||||
var nextLong = *(long*)(_block.Next.DataFixedPtr + _block.Next.Start);
|
||||
|
||||
return (blockLong >> (sizeof(long) - blockBytes) * 8) | (nextLong << (sizeof(long) - nextBytes) * 8);
|
||||
}
|
||||
|
|
@ -266,22 +255,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
#if !DEBUG
|
||||
}
|
||||
#endif
|
||||
fixed (byte* ptr = &block.Array[index])
|
||||
|
||||
var pCurrent = (block.DataFixedPtr + index);
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
{
|
||||
var pCurrent = ptr;
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent < pEnd);
|
||||
}
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent < pEnd);
|
||||
|
||||
following = 0;
|
||||
break;
|
||||
|
|
@ -367,28 +354,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
#if !DEBUG
|
||||
}
|
||||
#endif
|
||||
fixed (byte* ptr = &block.Array[index])
|
||||
var pCurrent = (block.DataFixedPtr + index);
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
{
|
||||
var pCurrent = ptr;
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
if (*pCurrent == byte1)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte1;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent != pEnd);
|
||||
}
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
if (*pCurrent == byte1)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte1;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent != pEnd);
|
||||
|
||||
following = 0;
|
||||
break;
|
||||
|
|
@ -502,34 +486,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
#if !DEBUG
|
||||
}
|
||||
#endif
|
||||
fixed (byte* ptr = &block.Array[index])
|
||||
var pCurrent = (block.DataFixedPtr + index);
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
{
|
||||
var pCurrent = ptr;
|
||||
var pEnd = pCurrent + following;
|
||||
do
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
if (*pCurrent == byte0)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
if (*pCurrent == byte1)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte1;
|
||||
}
|
||||
if (*pCurrent == byte2)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte2;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent != pEnd);
|
||||
}
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte0;
|
||||
}
|
||||
if (*pCurrent == byte1)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte1;
|
||||
}
|
||||
if (*pCurrent == byte2)
|
||||
{
|
||||
_block = block;
|
||||
_index = index;
|
||||
return byte2;
|
||||
}
|
||||
pCurrent++;
|
||||
index++;
|
||||
} while (pCurrent != pEnd);
|
||||
|
||||
following = 0;
|
||||
break;
|
||||
|
|
@ -808,30 +789,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3;
|
||||
}
|
||||
|
||||
fixed (byte* pOutput = &block.Data.Array[block.End])
|
||||
var output = (block.DataFixedPtr + block.End);
|
||||
var copied = 0;
|
||||
for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4)
|
||||
{
|
||||
//this line is needed to allow output be an register var
|
||||
var output = pOutput;
|
||||
|
||||
var copied = 0;
|
||||
for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4)
|
||||
{
|
||||
*(output) = (byte)*(input);
|
||||
*(output + 1) = (byte)*(input + 1);
|
||||
*(output + 2) = (byte)*(input + 2);
|
||||
*(output + 3) = (byte)*(input + 3);
|
||||
output += 4;
|
||||
input += 4;
|
||||
}
|
||||
for (; input < inputEnd && copied < bytesLeftInBlock; copied++)
|
||||
{
|
||||
*(output++) = (byte)*(input++);
|
||||
}
|
||||
|
||||
blockIndex += copied;
|
||||
bytesLeftInBlockMinusSpan -= copied;
|
||||
bytesLeftInBlock -= copied;
|
||||
*(output) = (byte)*(input);
|
||||
*(output + 1) = (byte)*(input + 1);
|
||||
*(output + 2) = (byte)*(input + 2);
|
||||
*(output + 3) = (byte)*(input + 3);
|
||||
output += 4;
|
||||
input += 4;
|
||||
}
|
||||
for (; input < inputEnd && copied < bytesLeftInBlock; copied++)
|
||||
{
|
||||
*(output++) = (byte)*(input++);
|
||||
}
|
||||
|
||||
blockIndex += copied;
|
||||
bytesLeftInBlockMinusSpan -= copied;
|
||||
bytesLeftInBlock -= copied;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,9 +116,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
|
||||
if (following > 0)
|
||||
{
|
||||
fixed (byte* blockStart = block.Array)
|
||||
{
|
||||
var input = blockStart + inputOffset;
|
||||
var input = block.DataFixedPtr + inputOffset;
|
||||
var i = 0;
|
||||
while (i < following - 11)
|
||||
{
|
||||
|
|
@ -167,9 +165,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
|||
output++;
|
||||
input++;
|
||||
}
|
||||
|
||||
|
||||
remaining -= following;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining == 0)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
|
||||
// create and pin each segment being written
|
||||
pBuffers[index] = Libuv.buf_init(
|
||||
block.Pin() + blockStart,
|
||||
block.DataArrayPtr + blockStart,
|
||||
blockEnd - blockStart);
|
||||
|
||||
block = block.Next;
|
||||
|
|
@ -89,7 +89,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
var block = start.Block;
|
||||
for (var index = 0; index < nBuffers; index++)
|
||||
{
|
||||
block.Unpin();
|
||||
block = block.Next;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,23 +14,27 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
private void FullByteRangeSupported()
|
||||
{
|
||||
var byteRange = Enumerable.Range(0, 256).Select(x => (byte)x).ToArray();
|
||||
|
||||
var mem = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
mem.End = byteRange.Length;
|
||||
|
||||
var begin = mem.GetIterator();
|
||||
var end = GetIterator(begin, byteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(s.Length, byteRange.Length);
|
||||
|
||||
for (var i = 0; i < byteRange.Length; i++)
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = byteRange[i];
|
||||
var mem = pool.Lease();
|
||||
mem.GetIterator().CopyFrom(byteRange);
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
var begin = mem.GetIterator();
|
||||
var end = GetIterator(begin, byteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(s.Length, byteRange.Length);
|
||||
|
||||
for (var i = 0; i < byteRange.Length; i++)
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = byteRange[i];
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
}
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,32 +48,40 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
.Concat(byteRange)
|
||||
.ToArray();
|
||||
|
||||
var mem0 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
var mem1 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
var mem2 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
var mem3 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
mem0.End = byteRange.Length;
|
||||
mem1.End = byteRange.Length;
|
||||
mem2.End = byteRange.Length;
|
||||
mem3.End = byteRange.Length;
|
||||
|
||||
mem0.Next = mem1;
|
||||
mem1.Next = mem2;
|
||||
mem2.Next = mem3;
|
||||
|
||||
var begin = mem0.GetIterator();
|
||||
var end = GetIterator(begin, expectedByteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(s.Length, expectedByteRange.Length);
|
||||
|
||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = expectedByteRange[i];
|
||||
var mem0 = pool.Lease();
|
||||
var mem1 = pool.Lease();
|
||||
var mem2 = pool.Lease();
|
||||
var mem3 = pool.Lease();
|
||||
mem0.GetIterator().CopyFrom(byteRange);
|
||||
mem1.GetIterator().CopyFrom(byteRange);
|
||||
mem2.GetIterator().CopyFrom(byteRange);
|
||||
mem3.GetIterator().CopyFrom(byteRange);
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
mem0.Next = mem1;
|
||||
mem1.Next = mem2;
|
||||
mem2.Next = mem3;
|
||||
|
||||
var begin = mem0.GetIterator();
|
||||
var end = GetIterator(begin, expectedByteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(s.Length, expectedByteRange.Length);
|
||||
|
||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = expectedByteRange[i];
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
}
|
||||
|
||||
pool.Return(mem0);
|
||||
pool.Return(mem1);
|
||||
pool.Return(mem2);
|
||||
pool.Return(mem3);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,27 +90,45 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)x).ToArray();
|
||||
var expectedByteRange = byteRange.Concat(byteRange).ToArray();
|
||||
|
||||
var mem0 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
var mem1 = MemoryPoolBlock.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
|
||||
mem0.End = byteRange.Length;
|
||||
mem1.End = byteRange.Length;
|
||||
|
||||
mem0.Next = mem1;
|
||||
|
||||
var begin = mem0.GetIterator();
|
||||
var end = GetIterator(begin, expectedByteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(s.Length, expectedByteRange.Length);
|
||||
|
||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = expectedByteRange[i];
|
||||
var mem0 = pool.Lease();
|
||||
var mem1 = pool.Lease();
|
||||
mem0.GetIterator().CopyFrom(byteRange);
|
||||
mem1.GetIterator().CopyFrom(byteRange);
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
var lastBlock = mem0;
|
||||
while (lastBlock.Next != null)
|
||||
{
|
||||
lastBlock = lastBlock.Next;
|
||||
}
|
||||
lastBlock.Next = mem1;
|
||||
|
||||
var begin = mem0.GetIterator();
|
||||
var end = GetIterator(begin, expectedByteRange.Length);
|
||||
|
||||
var s = begin.GetAsciiString(end);
|
||||
|
||||
Assert.Equal(expectedByteRange.Length, s.Length);
|
||||
|
||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
||||
{
|
||||
var sb = (byte)s[i];
|
||||
var b = expectedByteRange[i];
|
||||
|
||||
Assert.Equal(sb, b);
|
||||
}
|
||||
|
||||
var block = mem0;
|
||||
while (block != null)
|
||||
{
|
||||
var returnBlock = block;
|
||||
block = block.Next;
|
||||
pool.Return(returnBlock);
|
||||
}
|
||||
|
||||
pool.Return(mem0);
|
||||
pool.Return(mem1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block = pool.Lease(256);
|
||||
var block = pool.Lease();
|
||||
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
|
||||
{
|
||||
block.Array[block.End++] = ch;
|
||||
|
|
@ -63,9 +63,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block1 = pool.Lease(256);
|
||||
var block2 = block1.Next = pool.Lease(256);
|
||||
var block3 = block2.Next = pool.Lease(256);
|
||||
var block1 = pool.Lease();
|
||||
var block2 = block1.Next = pool.Lease();
|
||||
var block3 = block2.Next = pool.Lease();
|
||||
|
||||
foreach (var ch in Enumerable.Range(0, 34).Select(x => (byte)x))
|
||||
{
|
||||
|
|
@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block = pool.Lease(256);
|
||||
var block = pool.Lease();
|
||||
block.End += 256;
|
||||
TestAllLengths(block, 256);
|
||||
pool.Return(block);
|
||||
|
|
@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
for (var fragment = 0; fragment < 256; fragment += 4)
|
||||
{
|
||||
var next = block;
|
||||
block = pool.Lease(4);
|
||||
block = pool.Lease();
|
||||
block.Next = next;
|
||||
block.End += 4;
|
||||
}
|
||||
|
|
@ -168,8 +168,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block1 = pool.Lease(256);
|
||||
var block2 = block1.Next = pool.Lease(256);
|
||||
var block1 = pool.Lease();
|
||||
var block2 = block1.Next = pool.Lease();
|
||||
|
||||
block1.End += 100;
|
||||
block2.End += 200;
|
||||
|
|
@ -209,8 +209,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block1 = pool.Lease(128);
|
||||
var block2 = block1.Next = pool.Lease(128);
|
||||
var block1 = pool.Lease();
|
||||
var block2 = block1.Next = pool.Lease();
|
||||
|
||||
for (int i = 0; i < 128; i++)
|
||||
{
|
||||
|
|
@ -247,7 +247,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block1 = pool.Lease(128);
|
||||
var block1 = pool.Lease();
|
||||
var start = block1.GetIterator();
|
||||
var end = start;
|
||||
var bufferSize = block1.Data.Count * 3;
|
||||
|
|
@ -289,10 +289,10 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var block1 = pool.Lease(128);
|
||||
var block2 = block1.Next = pool.Lease(128);
|
||||
var block3 = block2.Next = pool.Lease(128);
|
||||
var block4 = block3.Next = pool.Lease(128);
|
||||
var block1 = pool.Lease();
|
||||
var block2 = block1.Next = pool.Lease();
|
||||
var block3 = block2.Next = pool.Lease();
|
||||
var block4 = block3.Next = pool.Lease();
|
||||
|
||||
// There is no data in block2 or block4, so IsEnd should be true after 256 bytes are read.
|
||||
block1.End += 128;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)]
|
||||
public void MemorySeek(string raw, string search, char expectResult, int expectIndex)
|
||||
{
|
||||
var block = _pool.Lease(256);
|
||||
var block = _pool.Lease();
|
||||
var chars = raw.ToCharArray().Select(c => (byte)c).ToArray();
|
||||
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
|
||||
block.End += chars.Length;
|
||||
|
|
@ -126,7 +126,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
var blocks = new MemoryPoolBlock[4];
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
blocks[i] = _pool.Lease(16);
|
||||
blocks[i] = _pool.Lease();
|
||||
blocks[i].End += 16;
|
||||
|
||||
for (var j = 0; j < blocks.Length; ++j)
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var writeRequest = new UvWriteReq(new KestrelTrace(new TestKestrelTrace()));
|
||||
writeRequest.Init(loop);
|
||||
var block = MemoryPoolBlock.Create(
|
||||
new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }),
|
||||
dataPtr: IntPtr.Zero,
|
||||
pool: null,
|
||||
slab: null);
|
||||
|
||||
var pool = new MemoryPool();
|
||||
var block = pool.Lease();
|
||||
block.GetIterator().CopyFrom(new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }));
|
||||
|
||||
var start = new MemoryPoolIterator(block, 0);
|
||||
var end = new MemoryPoolIterator(block, block.Data.Count);
|
||||
writeRequest.Write(
|
||||
|
|
@ -81,7 +81,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
writeRequest.Dispose();
|
||||
serverConnectionPipe.Dispose();
|
||||
serverListenPipe.Dispose();
|
||||
block.Unpin();
|
||||
pool.Return(block);
|
||||
pool.Dispose();
|
||||
},
|
||||
null);
|
||||
|
||||
|
|
|
|||
|
|
@ -177,12 +177,10 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
for (var x = 0; x < 2; x++)
|
||||
{
|
||||
var req = new UvWriteReq(new KestrelTrace(new TestKestrelTrace()));
|
||||
req.Init(loop);
|
||||
var block = MemoryPoolBlock.Create(
|
||||
new ArraySegment<byte>(new byte[] { 65, 66, 67, 68, 69 }),
|
||||
dataPtr: IntPtr.Zero,
|
||||
pool: null,
|
||||
slab: null);
|
||||
req.Init(loop); var pool = new MemoryPool();
|
||||
var block = pool.Lease();
|
||||
block.GetIterator().CopyFrom(new ArraySegment<byte>(new byte[] { 65, 66, 67, 68, 69 }));
|
||||
|
||||
var start = new MemoryPoolIterator(block, 0);
|
||||
var end = new MemoryPoolIterator(block, block.Data.Count);
|
||||
req.Write(
|
||||
|
|
@ -192,7 +190,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
1,
|
||||
(_1, _2, _3, _4) =>
|
||||
{
|
||||
block.Unpin();
|
||||
pool.Return(block);
|
||||
pool.Dispose();
|
||||
},
|
||||
null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,27 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[Fact]
|
||||
public void Empty()
|
||||
{
|
||||
PositiveAssert(string.Empty, string.Empty);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, string.Empty, string.Empty);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhiteSpace()
|
||||
{
|
||||
PositiveAssert(" ", " ");
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, " ", " ");
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -30,7 +44,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("/", "/")]
|
||||
public void NormalCases(string raw, string expect)
|
||||
{
|
||||
PositiveAssert(raw, expect);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, raw, expect);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -39,7 +60,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("/foo%2F%20bar", "/foo%2F bar")]
|
||||
public void SkipForwardSlash(string raw, string expect)
|
||||
{
|
||||
PositiveAssert(raw, expect);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, raw, expect);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -60,7 +88,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("%20", " ")]
|
||||
public void ValidUTF8(string raw, string expect)
|
||||
{
|
||||
PositiveAssert(raw, expect);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, raw, expect);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -68,7 +103,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("%E6%88%91%E8%87%AA%E6%A8%AA%E5%88%80%E5%90%91%E5%A4%A9%E7%AC%91%E5%8E%BB%E7%95%99%E8%82%9D%E8%83%86%E4%B8%A4%E6%98%86%E4%BB%91", "我自横刀向天笑去留肝胆两昆仑")]
|
||||
public void Internationalized(string raw, string expect)
|
||||
{
|
||||
PositiveAssert(raw, expect);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, raw, expect);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -92,7 +134,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("%C0%32%A4", "%C02%A4")]
|
||||
public void InvalidUTF8(string raw, string expect)
|
||||
{
|
||||
PositiveAssert(raw, expect);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
PositiveAssert(mem, raw, expect);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -110,21 +159,27 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("%C2%B5%40%C3%9F%C3%B6%C3%A4%C3%BC%C3%A0%C3%A1", 44, "µ@ßöäüà%C3%A", 12)]
|
||||
public void DecodeWithBoundary(string raw, int rawLength, string expect, int expectLength)
|
||||
{
|
||||
var begin = BuildSample(raw);
|
||||
var end = GetIterator(begin, rawLength);
|
||||
using (var pool = new MemoryPool())
|
||||
{
|
||||
var mem = pool.Lease();
|
||||
|
||||
var end2 = UrlPathDecoder.Unescape(begin, end);
|
||||
var result = begin.GetUtf8String(end2);
|
||||
var begin = BuildSample(mem, raw);
|
||||
var end = GetIterator(begin, rawLength);
|
||||
|
||||
Assert.Equal(expectLength, result.Length);
|
||||
Assert.Equal(expect, result);
|
||||
var end2 = UrlPathDecoder.Unescape(begin, end);
|
||||
var result = begin.GetUtf8String(end2);
|
||||
|
||||
Assert.Equal(expectLength, result.Length);
|
||||
Assert.Equal(expect, result);
|
||||
|
||||
pool.Return(mem);
|
||||
}
|
||||
}
|
||||
|
||||
private MemoryPoolIterator BuildSample(string data)
|
||||
private MemoryPoolIterator BuildSample(MemoryPoolBlock mem, string data)
|
||||
{
|
||||
var store = data.Select(c => (byte)c).ToArray();
|
||||
var mem = MemoryPoolBlock.Create(new ArraySegment<byte>(store), IntPtr.Zero, null, null);
|
||||
mem.End = store.Length;
|
||||
mem.GetIterator().CopyFrom(new ArraySegment<byte>(store));
|
||||
|
||||
return mem.GetIterator();
|
||||
}
|
||||
|
|
@ -140,27 +195,27 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
return result;
|
||||
}
|
||||
|
||||
private void PositiveAssert(string raw, string expect)
|
||||
private void PositiveAssert(MemoryPoolBlock mem, string raw, string expect)
|
||||
{
|
||||
var begin = BuildSample(raw);
|
||||
var begin = BuildSample(mem, raw);
|
||||
var end = GetIterator(begin, raw.Length);
|
||||
|
||||
var result = UrlPathDecoder.Unescape(begin, end);
|
||||
Assert.Equal(expect, begin.GetUtf8String(result));
|
||||
}
|
||||
|
||||
private void PositiveAssert(string raw)
|
||||
private void PositiveAssert(MemoryPoolBlock mem, string raw)
|
||||
{
|
||||
var begin = BuildSample(raw);
|
||||
var begin = BuildSample(mem, raw);
|
||||
var end = GetIterator(begin, raw.Length);
|
||||
|
||||
var result = UrlPathDecoder.Unescape(begin, end);
|
||||
Assert.NotEqual(raw.Length, begin.GetUtf8String(result).Length);
|
||||
}
|
||||
|
||||
private void NegativeAssert(string raw)
|
||||
private void NegativeAssert(MemoryPoolBlock mem, string raw)
|
||||
{
|
||||
var begin = BuildSample(raw);
|
||||
var begin = BuildSample(mem, raw);
|
||||
var end = GetIterator(begin, raw.Length);
|
||||
|
||||
var resultEnd = UrlPathDecoder.Unescape(begin, end);
|
||||
|
|
|
|||
Loading…
Reference in New Issue