Use the array pool by default instead of going through the MemoryPool (dotnet/corefx...
authorDavid Fowler <davidfowl@gmail.com>
Fri, 8 Feb 2019 20:01:35 +0000 (12:01 -0800)
committerStephen Toub <stoub@microsoft.com>
Fri, 8 Feb 2019 20:01:35 +0000 (15:01 -0500)
* Use the array pool by default instead of going through the MemoryPool<T> abstraction.

* Math.Clamp isn't in netstandard2.0

* Some cleanup based on PR feedback
- Keep setting MemoryPool<byte>.Shared by default
- Fixed the pipelines solution file

Commit migrated from https://github.com/dotnet/corefx/commit/e58f923745d37a5ce5a23ce838833d1e3455e62a

src/libraries/System.IO.Pipelines/System.IO.Pipelines.sln
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs
src/libraries/System.IO.Pipelines/tests/PipeOptionsTests.cs

index 2069232..fbff0b5 100644 (file)
@@ -20,7 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E89
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Pipelines.Performance.Tests", "tests\Performance\System.IO.Pipelines.Performance.Tests.csproj", "{66AE57BA-B56C-4A1E-ACA6-7C18431D416B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Pipelines.PerformanceTests", "tests\Performance\System.IO.Pipelines.PerformanceTests.csproj", "{66AE57BA-B56C-4A1E-ACA6-7C18431D416B}"
        ProjectSection(ProjectDependencies) = postProject
                {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} = {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}
        EndProjectSection
index 3c4c716..f416e26 100644 (file)
@@ -10,7 +10,7 @@ namespace System.IO.Pipelines
 {
     internal sealed class BufferSegment : ReadOnlySequenceSegment<byte>
     {
-        private IMemoryOwner<byte> _memoryOwner;
+        private object _memoryOwner;
         private BufferSegment _next;
         private int _end;
 
@@ -51,7 +51,17 @@ namespace System.IO.Pipelines
         {
             _memoryOwner = memoryOwner;
 
-            AvailableMemory = _memoryOwner.Memory;
+            AvailableMemory = memoryOwner.Memory;
+            RunningIndex = 0;
+            End = 0;
+            NextSegment = null;
+        }
+
+        public void SetMemory(byte[] arrayPoolBuffer)
+        {
+            _memoryOwner = arrayPoolBuffer;
+
+            AvailableMemory = arrayPoolBuffer;
             RunningIndex = 0;
             End = 0;
             NextSegment = null;
@@ -59,12 +69,21 @@ namespace System.IO.Pipelines
 
         public void ResetMemory()
         {
-            _memoryOwner.Dispose();
+            if (_memoryOwner is IMemoryOwner<byte> owner)
+            {
+                owner.Dispose();
+            }
+            else
+            {
+                ArrayPool<byte>.Shared.Return((byte[])_memoryOwner);
+            }
+
             _memoryOwner = null;
             AvailableMemory = default;
         }
 
-        internal IMemoryOwner<byte> MemoryOwner => _memoryOwner;
+        // Exposed for testing
+        internal object MemoryOwner => _memoryOwner;
 
         public Memory<byte> AvailableMemory { get; private set; }
 
index 209a482..7d7de54 100644 (file)
@@ -100,7 +100,9 @@ namespace System.IO.Pipelines
             _readerCompletion = default;
             _writerCompletion = default;
 
-            _pool = options.Pool;
+            // If we're using the default pool then mark it as null since we're just going to use the 
+            // array pool under the covers
+            _pool = options.Pool == MemoryPool<byte>.Shared ? null : options.Pool;
             _minimumSegmentSize = options.MinimumSegmentSize;
             _pauseWriterThreshold = options.PauseWriterThreshold;
             _resumeWriterThreshold = options.ResumeWriterThreshold;
@@ -179,8 +181,7 @@ namespace System.IO.Pipelines
             if (_writingHead == null)
             {
                 // We need to allocate memory to write since nobody has written before
-                BufferSegment newSegment = CreateSegmentUnsynchronized();
-                newSegment.SetMemory(_pool.Rent(GetSegmentSize(sizeHint)));
+                BufferSegment newSegment = AllocateSegment(sizeHint);
 
                 // Set all the pointers
                 _writingHead = _readHead = _readTail = newSegment;
@@ -191,8 +192,7 @@ namespace System.IO.Pipelines
 
                 if (bytesLeftInBuffer == 0 || bytesLeftInBuffer < sizeHint)
                 {
-                    BufferSegment newSegment = CreateSegmentUnsynchronized();
-                    newSegment.SetMemory(_pool.Rent(GetSegmentSize(sizeHint)));
+                    BufferSegment newSegment = AllocateSegment(sizeHint);
 
                     _writingHead.SetNext(newSegment);
                     _writingHead = newSegment;
@@ -200,12 +200,28 @@ namespace System.IO.Pipelines
             }
         }
 
-        private int GetSegmentSize(int sizeHint)
+        private BufferSegment AllocateSegment(int sizeHint)
+        {
+            BufferSegment newSegment = CreateSegmentUnsynchronized();
+
+            if (_pool is null)
+            {
+                newSegment.SetMemory(ArrayPool<byte>.Shared.Rent(GetSegmentSize(sizeHint)));
+            }
+            else
+            {
+                newSegment.SetMemory(_pool.Rent(GetSegmentSize(sizeHint, _pool.MaxBufferSize)));
+            }
+
+            return newSegment;
+        }
+
+        private int GetSegmentSize(int sizeHint, int maxBufferSize = int.MaxValue)
         {
             // First we need to handle case where hint is smaller than minimum segment size
             sizeHint = Math.Max(_minimumSegmentSize, sizeHint);
             // After that adjust it to fit into pools max buffer size
-            var adjustedToMaximumSize = Math.Min(_pool.MaxBufferSize, sizeHint);
+            var adjustedToMaximumSize = Math.Min(maxBufferSize, sizeHint);
             return adjustedToMaximumSize;
         }
 
index 5e6202f..0becde7 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Buffers;
 using Xunit;
 
 namespace System.IO.Pipelines.Tests
@@ -19,5 +20,11 @@ namespace System.IO.Pipelines.Tests
         {
             Assert.Equal(16384, PipeOptions.Default.ResumeWriterThreshold);
         }
+
+        [Fact]
+        public void DefaultPoolIsSetToShared()
+        {
+            Assert.Equal(MemoryPool<byte>.Shared, PipeOptions.Default.Pool);
+        }
     }
 }