Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mscorlib/System.Private.CoreLib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Private.CoreLib", "System.Private.CoreLib.csproj", "{3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E16B1C86-C275-495B-80D6-7CE8196A18B4}"
ProjectSection(SolutionItems) = preProject
model.xml = model.xml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|amd64 = Checked|amd64
Expand Down
9 changes: 9 additions & 0 deletions src/mscorlib/model.xml
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,15 @@
<Member Name="MemoryCopy(System.Void*,System.Void*,System.Int64,System.Int64)" />
<Member Name="MemoryCopy(System.Void*,System.Void*,System.UInt64,System.UInt64)" />
</Type>
<Type Name="System.Buffers.ArrayPool&lt;T&gt;">
<Member Name="#ctor" />
<Member Name="Create" />
<Member Name="Create(System.Int32,System.Int32)" />
<Member Name="get_Shared" />
<Member MemberType="Property" Name="Shared" />
<Member Name="Rent(System.Int32)" />
<Member Name="Return(T[],System.Boolean)" />
</Type>
<Type Name="System.Byte">
<Member MemberType="Field" Name="MaxValue" />
<Member MemberType="Field" Name="MinValue" />
Expand Down
8 changes: 8 additions & 0 deletions src/mscorlib/mscorlib.shared.sources.props
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,13 @@
<ItemGroup>
<NumericsSources Include="$(BclSourcesRoot)\System\Numerics\Hashing\HashHelpers.cs" />
</ItemGroup>
<ItemGroup>
<BuffersSources Include="$(BclSourcesRoot)\System\Buffers\ArrayPool.cs" />
<BuffersSources Include="$(BclSourcesRoot)\System\Buffers\ArrayPoolEventSource.cs" />
<BuffersSources Include="$(BclSourcesRoot)\System\Buffers\DefaultArrayPool.cs" />
<BuffersSources Include="$(BclSourcesRoot)\System\Buffers\DefaultArrayPoolBucket.cs" />
<BuffersSources Include="$(BclSourcesRoot)\System\Buffers\Utilities.cs" />
</ItemGroup>
<ItemGroup>
<MscorlibSources Include="@(SystemSources)"/>
<MscorlibSources Include="@(ThreadingSources)"/>
Expand Down Expand Up @@ -1184,5 +1191,6 @@
<MscorlibSources Include="@(InternalSources)"/>
<MscorlibSources Include="@(NumericsSources)"/>
<MscorlibSources Include="$(BclSourcesRoot)\GlobalSuppressions.cs"/>
<MscorlibSources Include="@(BuffersSources)"/>
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions src/mscorlib/src/System.Private.CoreLib.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,9 @@ InvalidOperation_CollectionBackingListTooLarge=The collection backing this List
InvalidOperation_CollectionBackingDictionaryTooLarge=The collection backing this Dictionary contains too many elements.
InvalidOperation_CannotRemoveLastFromEmptyCollection=Cannot remove the last element from an empty collection.

; Buffers
ArgumentException_BufferNotFromPool=The buffer is not associated with this pool and may not be returned to it.

; Globalization resources
;------------------

Expand Down
118 changes: 118 additions & 0 deletions src/mscorlib/src/System/Buffers/ArrayPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.Runtime.CompilerServices;
using System.Threading;

namespace System.Buffers
{
/// <summary>
/// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>.
/// </summary>
/// <remarks>
/// <para>
/// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
/// in situations where arrays are created and destroyed frequently, resulting in significant
/// memory pressure on the garbage collector.
/// </para>
/// <para>
/// This class is thread-safe. All members may be used by multiple threads concurrently.
/// </para>
/// </remarks>
public abstract class ArrayPool<T>
{
/// <summary>The lazily-initialized shared pool instance.</summary>
private static ArrayPool<T> s_sharedInstance = null;

/// <summary>
/// Retrieves a shared <see cref="ArrayPool{T}"/> instance.
/// </summary>
/// <remarks>
/// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
/// that's intended for general applicability. It maintains arrays of multiple sizes, and
/// may hand back a larger array than was actually requested, but will never hand back a smaller
/// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an
/// existing buffer being taken from the pool if an appropriate buffer is available or in a new
/// buffer being allocated if one is not available.
/// </remarks>
public static ArrayPool<T> Shared
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); }
}

/// <summary>Ensures that <see cref="s_sharedInstance"/> has been initialized to a pool and returns it.</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private static ArrayPool<T> EnsureSharedCreated()
{
Interlocked.CompareExchange(ref s_sharedInstance, Create(), null);
return s_sharedInstance;
}

/// <summary>
/// Creates a new <see cref="ArrayPool{T}"/> instance using default configuration options.
/// </summary>
/// <returns>A new <see cref="ArrayPool{T}"/> instance.</returns>
public static ArrayPool<T> Create()
{
return new DefaultArrayPool<T>();
}

/// <summary>
/// Creates a new <see cref="ArrayPool{T}"/> instance using custom configuration options.
/// </summary>
/// <param name="maxArrayLength">The maximum length of array instances that may be stored in the pool.</param>
/// <param name="maxArraysPerBucket">
/// The maximum number of array instances that may be stored in each bucket in the pool. The pool
/// groups arrays of similar lengths into buckets for faster access.
/// </param>
/// <returns>A new <see cref="ArrayPool{T}"/> instance with the specified configuration options.</returns>
/// <remarks>
/// The created pool will group arrays into buckets, with no more than <paramref name="maxArraysPerBucket"/>
/// in each bucket and with those arrays not exceeding <paramref name="maxArrayLength"/> in length.
/// </remarks>
public static ArrayPool<T> Create(int maxArrayLength, int maxArraysPerBucket)
{
return new DefaultArrayPool<T>(maxArrayLength, maxArraysPerBucket);
}

/// <summary>
/// Retrieves a buffer that is at least the requested length.
/// </summary>
/// <param name="minimumLength">The minimum length of the array needed.</param>
/// <returns>
/// An <see cref="T:T[]"/> that is at least <paramref name="minimumLength"/> in length.
/// </returns>
/// <remarks>
/// This buffer is loaned to the caller and should be returned to the same pool via
/// <see cref="Return"/> so that it may be reused in subsequent usage of <see cref="Rent"/>.
/// It is not a fatal error to not return a rented buffer, but failure to do so may lead to
/// decreased application performance, as the pool may need to create a new buffer to replace
/// the one lost.
/// </remarks>
public abstract T[] Rent(int minimumLength);

/// <summary>
/// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same
/// <see cref="ArrayPool{T}"/> instance.
/// </summary>
/// <param name="array">
/// The buffer previously obtained from <see cref="Rent"/> to return to the pool.
/// </param>
/// <param name="clearArray">
/// If <c>true</c> and if the pool will store the buffer to enable subsequent reuse, <see cref="Return"/>
/// will clear <paramref name="array"/> of its contents so that a subsequent consumer via <see cref="Rent"/>
/// will not see the previous consumer's content. If <c>false</c> or if the pool will release the buffer,
/// the array's contents are left unchanged.
/// </param>
/// <remarks>
/// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer
/// and must not use it. The reference returned from a given call to <see cref="Rent"/> must only be
/// returned via <see cref="Return"/> once. The default <see cref="ArrayPool{T}"/>
/// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer
/// if it's determined that the pool already has enough buffers stored.
/// </remarks>
public abstract void Return(T[] array, bool clearArray = false);
}
}
78 changes: 78 additions & 0 deletions src/mscorlib/src/System/Buffers/ArrayPoolEventSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.Diagnostics.Tracing;

namespace System.Buffers
{
[EventSource(Name = "System.Buffers.ArrayPoolEventSource")]
internal sealed class ArrayPoolEventSource : EventSource
{
internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource();

/// <summary>The reason for a BufferAllocated event.</summary>
internal enum BufferAllocatedReason : int
{
/// <summary>The pool is allocating a buffer to be pooled in a bucket.</summary>
Pooled,
/// <summary>The requested buffer size was too large to be pooled.</summary>
OverMaximumSize,
/// <summary>The pool has already allocated for pooling as many buffers of a particular size as it's allowed.</summary>
PoolExhausted
}

/// <summary>
/// Event for when a buffer is rented. This is invoked once for every successful call to Rent,
/// regardless of whether a buffer is allocated or a buffer is taken from the pool. In a
/// perfect situation where all rented buffers are returned, we expect to see the number
/// of BufferRented events exactly match the number of BuferReturned events, with the number
/// of BufferAllocated events being less than or equal to those numbers (ideally significantly
/// less than).
/// </summary>
[Event(1, Level = EventLevel.Verbose)]
internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId)
{
EventData* payload = stackalloc EventData[4];
payload[0].Size = sizeof(int);
payload[0].DataPointer = ((IntPtr)(&bufferId));
payload[1].Size = sizeof(int);
payload[1].DataPointer = ((IntPtr)(&bufferSize));
payload[2].Size = sizeof(int);
payload[2].DataPointer = ((IntPtr)(&poolId));
payload[3].Size = sizeof(int);
payload[3].DataPointer = ((IntPtr)(&bucketId));
WriteEventCore(1, 4, payload);
}

/// <summary>
/// Event for when a buffer is allocated by the pool. In an ideal situation, the number
/// of BufferAllocated events is significantly smaller than the number of BufferRented and
/// BufferReturned events.
/// </summary>
[Event(2, Level = EventLevel.Informational)]
internal unsafe void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason)
{
EventData* payload = stackalloc EventData[5];
payload[0].Size = sizeof(int);
payload[0].DataPointer = ((IntPtr)(&bufferId));
payload[1].Size = sizeof(int);
payload[1].DataPointer = ((IntPtr)(&bufferSize));
payload[2].Size = sizeof(int);
payload[2].DataPointer = ((IntPtr)(&poolId));
payload[3].Size = sizeof(int);
payload[3].DataPointer = ((IntPtr)(&bucketId));
payload[4].Size = sizeof(BufferAllocatedReason);
payload[4].DataPointer = ((IntPtr)(&reason));
WriteEventCore(2, 5, payload);
}

/// <summary>
/// Event raised when a buffer is returned to the pool. This event is raised regardless of whether
/// the returned buffer is stored or dropped. In an ideal situation, the number of BufferReturned
/// events exactly matches the number of BufferRented events.
/// </summary>
[Event(3, Level = EventLevel.Verbose)]
internal void BufferReturned(int bufferId, int bufferSize, int poolId) => WriteEvent(3, bufferId, bufferSize, poolId);
}
}
Loading