From a31d847719dcc16507a8c387650f44c64aeae12b Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 30 Apr 2016 11:16:24 +0100 Subject: [PATCH 1/3] Tests only to use pooled blocks --- .../AsciiDecoder.cs | 146 +++++++++++------- .../MemoryPoolBlockTests.cs | 30 ++-- .../MemoryPoolIteratorTests.cs | 4 +- .../MultipleLoopTests.cs | 13 +- .../NetworkingTests.cs | 13 +- .../UrlPathDecoder.cs | 99 +++++++++--- 6 files changed, 195 insertions(+), 110 deletions(-) diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoder.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoder.cs index 10e95d2e56..634efc65db 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoder.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoder.cs @@ -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(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(byteRange), IntPtr.Zero, null, null); - var mem1 = MemoryPoolBlock.Create(new ArraySegment(byteRange), IntPtr.Zero, null, null); - var mem2 = MemoryPoolBlock.Create(new ArraySegment(byteRange), IntPtr.Zero, null, null); - var mem3 = MemoryPoolBlock.Create(new ArraySegment(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(byteRange), IntPtr.Zero, null, null); - var mem1 = MemoryPoolBlock.Create(new ArraySegment(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); } } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs index 73bb3b2ef8..748a0ecf34 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs @@ -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; diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs index 18fdeb5afd..834c99baad 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs @@ -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) diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs index c5b8c30c5b..af50bba928 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs @@ -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(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(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); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs index c299331818..dd5c5044fc 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs @@ -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(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(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); } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs index 711297fd95..31e1ccdbc9 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs @@ -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(store), IntPtr.Zero, null, null); - mem.End = store.Length; + mem.GetIterator().CopyFrom(new ArraySegment(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); From 2aa10a64a270eaef9c1324173717a437402fba69 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 30 Apr 2016 10:01:39 +0100 Subject: [PATCH 2/3] Remove noop pin+unpin w/ memory blocks + Only use pooled blocks in tests --- .../Http/Connection.cs | 2 +- .../Http/SocketOutput.cs | 3 - .../Infrastructure/MemoryPool.cs | 17 +----- .../Infrastructure/MemoryPoolBlock.cs | 61 +++---------------- .../Networking/UvWriteReq.cs | 3 +- 5 files changed, 10 insertions(+), 76 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs index 814da8143b..add23c4389 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs @@ -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); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs index f424a7531c..5db2fb3ebc 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs @@ -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); } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPool.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPool.cs index d5a4e59b7e..6fd37f8a9c 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPool.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPool.cs @@ -63,24 +63,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure /// /// Called to take a block from the pool. /// - /// 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. /// The block that is reserved for the called. It must be passed to Return when it is no longer being used. - 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(new byte[minimumSize]), - dataPtr: IntPtr.Zero, - pool: this, - slab: null); - } - MemoryPoolBlock block; if (_blocks.TryDequeue(out block)) { diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs index 25cab8c4f4..362165ee12 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs @@ -11,18 +11,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure /// public class MemoryPoolBlock { - /// - /// 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. - /// - private GCHandle _pinHandle; - /// /// 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. /// - private IntPtr _dataArrayPtr; + public readonly IntPtr DataArrayPtr; /// /// 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 +27,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure /// /// This object cannot be instantiated outside of the static Create method /// - protected MemoryPoolBlock() + protected MemoryPoolBlock(IntPtr dataArrayPtr) { + DataArrayPtr = dataArrayPtr; } /// @@ -76,20 +71,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 +84,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure } } - /// - /// 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. - /// - /// - 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 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, diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvWriteReq.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvWriteReq.cs index bf088950a5..bc776767a6 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvWriteReq.cs @@ -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; } From 3a424f6abac6a019da4d00c32e98880c0f99e8d7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 30 Apr 2016 10:13:50 +0100 Subject: [PATCH 3/3] Remove unnecessary fixed blocks --- .../Infrastructure/MemoryPoolBlock.cs | 5 +- .../Infrastructure/MemoryPoolIterator.cs | 168 ++++++++---------- .../MemoryPoolIteratorExtensions.cs | 7 +- 3 files changed, 78 insertions(+), 102 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs index 362165ee12..3badd57fce 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolBlock.cs @@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure /// public readonly IntPtr DataArrayPtr; + internal unsafe readonly byte* DataFixedPtr; + /// /// The array segment describing the range of memory this block is tracking. The caller which has leased this block may only read and /// modify the memory in this range. @@ -27,9 +29,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure /// /// This object cannot be instantiated outside of the static Create method /// - protected MemoryPoolBlock(IntPtr dataArrayPtr) + unsafe protected MemoryPoolBlock(IntPtr dataArrayPtr) { DataArrayPtr = dataArrayPtr; + DataFixedPtr = (byte*)dataArrayPtr.ToPointer(); } /// diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIterator.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIterator.cs index 286862ce4b..0fcfe7f434 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIterator.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIterator.cs @@ -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; } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIteratorExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIteratorExtensions.cs index 1f9b3dcd7a..f17dab1394 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIteratorExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/MemoryPoolIteratorExtensions.cs @@ -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)