Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove System.ServiceModel.Primitives dependency. #1017

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
70 changes: 70 additions & 0 deletions src/NetMQ.Tests/BufferPoolTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// See BufferPool.cs for explanation of USE_SERVICE_MODEL define.

// #define USE_SERVICE_MODEL
using System;
using Xunit;

namespace NetMQ.Tests
{
public abstract class BufferPoolTests
{
protected int maxBufferSize = 2;
protected long maxBufferPoolSize = 100L;
// Silence the non-nullable warning.
#pragma warning disable 8618
protected IBufferPool pool;
#pragma warning restore 8618

[Theory]
[InlineData(500)]
[InlineData(50000)]
[InlineData(5000000)]
[InlineData(500000000)]
public void Rent(int size)
{
/* The pool sizes were chosen to be very small. It's clear they do
* not constitute any bounds on the array sizes that can be
* requested. BufferManagerBufferPool has the same behavior
* though. */
var array = pool.Take(size);
Assert.Equal(size, array.Length);
}

// I was not able to provoke this to happen. Maybe with a long.
// [Fact]
// public void RentTooBigBuffer()
// {
// Assert.ThrowsAny<Exception>(() => pool.Take(900000000));
// }

[Fact]
public void Return() {
var array = pool.Take(10);
pool.Return(array);
}

[Fact]
public void ReturnUnknown() {
var array = new byte[100];
Assert.Throws<ArgumentException>(() => pool.Return(array));
}
}

public class ArrayBufferPoolTests : BufferPoolTests
{
public ArrayBufferPoolTests()
{
pool = new ArrayBufferPool(maxBufferPoolSize, maxBufferSize);
}
}

#if USE_SERVICE_MODEL
public class BufferManagerBufferPoolTests : BufferPoolTests
{
public BufferManagerBufferPoolTests()
{
pool = new BufferManagerBufferPool(maxBufferPoolSize, maxBufferSize);
}
}
#endif
}
94 changes: 92 additions & 2 deletions src/NetMQ/BufferPool.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
// USE_SERVICE_MODEL requires the System.ServiceModel.Primitives package.
// It has been substituted by the System.Buffers package. This define remains
// merely to be able to exercise tests against both IBufferPool implementations.

// #define USE_SERVICE_MODEL
using System;
#if USE_SERVICE_MODEL
using System.ServiceModel.Channels;
#endif
using System.Collections.Generic;
using System.Threading;
using System.Buffers;

namespace NetMQ
{
Expand All @@ -24,6 +33,81 @@ public interface IBufferPool : IDisposable
void Return(byte[] buffer);
}

/// <summary>
/// This implementation of <see cref="IBufferPool"/> uses System.Buffer's ArrayPool
/// class to manage a pool of buffers.
/// </summary>
public class ArrayBufferPool : IBufferPool
{
private readonly ArrayPool<byte> m_arrayPool;
private readonly HashSet<byte[]> m_onLoan;

/// <summary>
/// Create a new ArrayBufferPool with the specified maximum buffer pool size
/// and a maximum size for each individual buffer in the pool.
/// </summary>
/// <param name="maxBufferPoolSize">the maximum size to allow for the buffer pool</param>
/// <param name="maxBufferSize">the maximum size to allow for each individual buffer in the pool</param>
/// <exception cref="InsufficientMemoryException">There was insufficient memory to create the requested buffer pool.</exception>
/// <exception cref="ArgumentOutOfRangeException">Either maxBufferPoolSize or maxBufferSize was less than zero.</exception>
public ArrayBufferPool(long maxBufferPoolSize, int maxBufferSize)
{
m_arrayPool = ArrayPool<byte>.Create(maxBufferSize, (int) maxBufferPoolSize);
m_onLoan = new HashSet<byte[]>();
}

/// <summary>
/// Return a byte-array buffer of at least the specified size from the pool.
/// </summary>
/// <param name="size">the size in bytes of the requested buffer</param>
/// <returns>a byte-array that is the requested size</returns>
/// <exception cref="ArgumentOutOfRangeException">size cannot be less than zero</exception>
public byte[] Take(int size)
{
var loaner = m_arrayPool.Rent(size);
m_onLoan.Add(loaner);
return loaner;
}

/// <summary>
/// Return the given buffer to this manager pool.
/// </summary>
/// <param name="buffer">a reference to the buffer being returned</param>
/// <exception cref="ArgumentException">the Length of buffer does not match the pool's buffer length property</exception>
/// <exception cref="ArgumentNullException">the buffer reference cannot be null</exception>
public void Return(byte[] buffer)
{
if (m_onLoan.Remove(buffer))
m_arrayPool.Return(buffer);
else
throw new ArgumentException("Buffer returned to pool was not rented.",
nameof(buffer));
}

/// <summary>
/// Release the buffers currently cached in this manager.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Release the buffers currently cached in this manager.
/// </summary>
/// <param name="disposing">true if managed resources are to be disposed</param>
protected virtual void Dispose(bool disposing)
{
if (!disposing)
return;
foreach (var loaner in m_onLoan)
Return(loaner);
m_onLoan.Clear();
}
}

#if USE_SERVICE_MODEL
/// <summary>
/// This implementation of <see cref="IBufferPool"/> uses WCF's <see cref="BufferManager"/>
/// class to manage a pool of buffers.
Expand Down Expand Up @@ -88,6 +172,7 @@ protected virtual void Dispose(bool disposing)
m_bufferManager.Clear();
}
}
#endif

/// <summary>
/// This simple implementation of <see cref="IBufferPool"/> does no buffer pooling. Instead, it uses regular
Expand Down Expand Up @@ -134,6 +219,7 @@ protected virtual void Dispose(bool disposing)
}
}


/// <summary>
/// Contains a singleton instance of <see cref="IBufferPool"/> used for allocating byte arrays
/// for <see cref="Msg"/> instances with type <see cref="MsgType.Pool"/>.
Expand All @@ -144,7 +230,7 @@ protected virtual void Dispose(bool disposing)
/// <para/>
/// The default implementation is <see cref="GCBufferPool"/>.
/// <list type="bullet">
/// <item>Call <see cref="SetBufferManagerBufferPool"/> to replace it with a <see cref="BufferManagerBufferPool"/>.</item>
/// <item>Call <see cref="SetBufferManagerBufferPool"/> to replace it with a pooling implementation.</item>
/// <item>Call <see cref="SetGCBufferPool"/> to reinstate the default <see cref="GCBufferPool"/>.</item>
/// <item>Call <see cref="SetCustomBufferPool"/> to substitute a custom implementation for the allocation and
/// deallocation of message buffers.</item>
Expand All @@ -163,15 +249,19 @@ public static void SetGCBufferPool()
}

/// <summary>
/// Set BufferPool to use the <see cref="BufferManagerBufferPool"/> to manage the buffer-pool.
/// Set BufferPool to use a buffer pooling implementation to manage the buffer-pool.
/// </summary>
/// <param name="maxBufferPoolSize">the maximum size to allow for the buffer pool</param>
/// <param name="maxBufferSize">the maximum size to allow for each individual buffer in the pool</param>
/// <exception cref="InsufficientMemoryException">There was insufficient memory to create the requested buffer pool.</exception>
/// <exception cref="ArgumentOutOfRangeException">Either maxBufferPoolSize or maxBufferSize was less than zero.</exception>
public static void SetBufferManagerBufferPool(long maxBufferPoolSize, int maxBufferSize)
{
#if USE_SERVICE_MODEL
SetCustomBufferPool(new BufferManagerBufferPool(maxBufferPoolSize, maxBufferSize));
#else
SetCustomBufferPool(new ArrayBufferPool(maxBufferPoolSize, maxBufferSize));
#endif
}

/// <summary>
Expand Down
5 changes: 2 additions & 3 deletions src/NetMQ/NetMQ.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' != 'netstandard2.1' ">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Expand All @@ -37,16 +37,15 @@
<PackageReference Include="AsyncIO" Version="0.1.69" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="NaCl.Net" Version="0.1.13" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.ServiceModel.Primitives" Version="4.9.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<PackageReference Include="System.ServiceModel.Primitives" Version="4.9.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>

Expand Down