Remove ObjectPool<T>, port changes from ComputeSharp
This commit is contained in:
Родитель
d718237031
Коммит
ced0aa7f28
|
@ -6,6 +6,9 @@
|
|||
// more info in ThirdPartyNotices.txt in the root of the project.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -16,13 +19,8 @@ namespace CommunityToolkit.Extensions.DependencyInjection.SourceGenerators.Helpe
|
|||
/// A helper type to build sequences of values with pooled buffers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items to create sequences for.</typeparam>
|
||||
internal struct ImmutableArrayBuilder<T> : IDisposable
|
||||
internal ref struct ImmutableArrayBuilder<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The shared <see cref="ObjectPool{T}"/> instance to share <see cref="Writer"/> objects.
|
||||
/// </summary>
|
||||
private static readonly ObjectPool<Writer> SharedObjectPool = new(static () => new Writer());
|
||||
|
||||
/// <summary>
|
||||
/// The rented <see cref="Writer"/> instance to use.
|
||||
/// </summary>
|
||||
|
@ -34,7 +32,7 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
/// <returns>A <see cref="ImmutableArrayBuilder{T}"/> instance to write data to.</returns>
|
||||
public static ImmutableArrayBuilder<T> Rent()
|
||||
{
|
||||
return new(SharedObjectPool.Allocate());
|
||||
return new(new Writer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -46,14 +44,21 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
this.writer = writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data written to the underlying buffer so far, as a <see cref="Span{T}"/>.
|
||||
/// </summary>
|
||||
[UnscopedRef]
|
||||
public readonly Span<T> Span
|
||||
/// <inheritdoc cref="ImmutableArray{T}.Builder.Count"/>
|
||||
public readonly int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this.writer!.Span;
|
||||
get => this.writer!.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
|
||||
/// </summary>
|
||||
[UnscopedRef]
|
||||
public readonly ReadOnlySpan<T> WrittenSpan
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this.writer!.WrittenSpan;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ImmutableArray{T}.Builder.Add(T)"/>
|
||||
|
@ -66,7 +71,7 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
/// Adds the specified items to the end of the array.
|
||||
/// </summary>
|
||||
/// <param name="items">The items to add at the end of the array.</param>
|
||||
public readonly void AddRange(ReadOnlySpan<T> items)
|
||||
public readonly void AddRange(scoped ReadOnlySpan<T> items)
|
||||
{
|
||||
this.writer!.AddRange(items);
|
||||
}
|
||||
|
@ -74,7 +79,7 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
/// <inheritdoc cref="ImmutableArray{T}.Builder.ToImmutable"/>
|
||||
public readonly ImmutableArray<T> ToImmutable()
|
||||
{
|
||||
T[] array = this.writer!.Span.ToArray();
|
||||
T[] array = this.writer!.WrittenSpan.ToArray();
|
||||
|
||||
return Unsafe.As<T[], ImmutableArray<T>>(ref array);
|
||||
}
|
||||
|
@ -82,39 +87,46 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
/// <inheritdoc cref="ImmutableArray{T}.Builder.ToArray"/>
|
||||
public readonly T[] ToArray()
|
||||
{
|
||||
return this.writer!.Span.ToArray();
|
||||
return this.writer!.WrittenSpan.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IEnumerable{T}"/> instance for the current builder.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IEnumerable{T}"/> instance for the current builder.</returns>
|
||||
/// <remarks>
|
||||
/// The builder should not be mutated while an enumerator is in use.
|
||||
/// </remarks>
|
||||
public readonly IEnumerable<T> AsEnumerable()
|
||||
{
|
||||
return this.writer!;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return this.writer!.Span.ToString();
|
||||
return this.writer!.WrittenSpan.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <inheritdoc cref="IDisposable.Dispose"/>
|
||||
public void Dispose()
|
||||
{
|
||||
Writer? writer = this.writer;
|
||||
|
||||
this.writer = null;
|
||||
|
||||
if (writer is not null)
|
||||
{
|
||||
writer.Clear();
|
||||
|
||||
SharedObjectPool.Free(writer);
|
||||
}
|
||||
writer?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class handling the actual buffer writing.
|
||||
/// </summary>
|
||||
private sealed class Writer
|
||||
private sealed class Writer : ICollection<T>, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying <typeparamref name="T"/> array.
|
||||
/// </summary>
|
||||
private T[] array;
|
||||
private T?[]? array;
|
||||
|
||||
/// <summary>
|
||||
/// The starting offset within <see cref="array"/>.
|
||||
|
@ -126,31 +138,33 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
/// </summary>
|
||||
public Writer()
|
||||
{
|
||||
if (typeof(T) == typeof(char))
|
||||
{
|
||||
this.array = new T[1024];
|
||||
}
|
||||
else
|
||||
{
|
||||
this.array = new T[8];
|
||||
}
|
||||
|
||||
this.array = ArrayPool<T?>.Shared.Rent(typeof(T) == typeof(char) ? 1024 : 8);
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ImmutableArrayBuilder{T}.Span"/>
|
||||
public Span<T> Span
|
||||
/// <inheritdoc cref="ImmutableArrayBuilder{T}.Count"/>
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(this.array, 0, this.index);
|
||||
get => this.index;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ImmutableArrayBuilder{T}.WrittenSpan"/>
|
||||
public ReadOnlySpan<T> WrittenSpan
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(this.array!, 0, this.index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ICollection<T>.IsReadOnly => true;
|
||||
|
||||
/// <inheritdoc cref="ImmutableArrayBuilder{T}.Add"/>
|
||||
public void Add(T value)
|
||||
{
|
||||
EnsureCapacity(1);
|
||||
|
||||
this.array[this.index++] = value;
|
||||
this.array![this.index++] = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ImmutableArrayBuilder{T}.AddRange"/>
|
||||
|
@ -158,22 +172,64 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
{
|
||||
EnsureCapacity(items.Length);
|
||||
|
||||
items.CopyTo(this.array.AsSpan(this.index));
|
||||
items.CopyTo(this.array.AsSpan(this.index)!);
|
||||
|
||||
this.index += items.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the items in the current writer.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (typeof(T) != typeof(char))
|
||||
{
|
||||
this.array.AsSpan(0, this.index).Clear();
|
||||
}
|
||||
T?[]? array = this.array;
|
||||
|
||||
this.index = 0;
|
||||
this.array = null;
|
||||
|
||||
if (array is not null)
|
||||
{
|
||||
ArrayPool<T?>.Shared.Return(array, clearArray: typeof(T) != typeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void ICollection<T>.Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ICollection<T>.Contains(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
Array.Copy(this.array!, 0, array, arrayIndex, this.index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
T?[] array = this.array!;
|
||||
int length = this.index;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
yield return array[i]!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)this).GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -183,7 +239,7 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int requestedSize)
|
||||
{
|
||||
if (requestedSize > this.array.Length - this.index)
|
||||
if (requestedSize > this.array!.Length - this.index)
|
||||
{
|
||||
ResizeBuffer(requestedSize);
|
||||
}
|
||||
|
@ -197,27 +253,15 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
|
|||
private void ResizeBuffer(int sizeHint)
|
||||
{
|
||||
int minimumSize = this.index + sizeHint;
|
||||
int requestedSize = Math.Max(this.array.Length * 2, minimumSize);
|
||||
|
||||
T[] newArray = new T[requestedSize];
|
||||
T?[] oldArray = this.array!;
|
||||
T?[] newArray = ArrayPool<T?>.Shared.Rent(minimumSize);
|
||||
|
||||
Array.Copy(this.array, newArray, this.index);
|
||||
Array.Copy(oldArray, newArray, this.index);
|
||||
|
||||
this.array = newArray;
|
||||
|
||||
ArrayPool<T?>.Shared.Return(oldArray, clearArray: typeof(T) != typeof(char));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private helpers for the <see cref="ImmutableArrayBuilder{T}"/> type.
|
||||
/// </summary>
|
||||
internal static class ImmutableArrayBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Throws an <see cref="ArgumentOutOfRangeException"/> for <c>"index"</c>.
|
||||
/// </summary>
|
||||
public static void ThrowArgumentOutOfRangeExceptionForIndex()
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// Ported from Roslyn, see: https://github.com/dotnet/roslyn/blob/main/src/Dependencies/PooledObjects/ObjectPool%601.cs.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace CommunityToolkit.Extensions.DependencyInjection.SourceGenerators.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Generic implementation of object pooling pattern with predefined pool size limit. The main purpose
|
||||
/// is that limited number of frequently used objects can be kept in the pool for further recycling.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Notes:
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// It is not the goal to keep all returned objects. Pool is not meant for storage. If there
|
||||
/// is no space in the pool, extra returned objects will be dropped.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// It is implied that if object was obtained from a pool, the caller will return it back in
|
||||
/// a relatively short time. Keeping checked out objects for long durations is ok, but
|
||||
/// reduces usefulness of pooling. Just new up your own.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice.
|
||||
/// Rationale: if there is no intent for reusing the object, do not use pool - just use "new".
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects to pool.</typeparam>
|
||||
internal sealed class ObjectPool<T>
|
||||
where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// The factory is stored for the lifetime of the pool. We will call this only when pool needs to
|
||||
/// expand. compared to "new T()", Func gives more flexibility to implementers and faster than "new T()".
|
||||
/// </summary>
|
||||
private readonly Func<T> factory;
|
||||
|
||||
/// <summary>
|
||||
/// The array of cached items.
|
||||
/// </summary>
|
||||
private readonly Element[] items;
|
||||
|
||||
/// <summary>
|
||||
/// Storage for the pool objects. The first item is stored in a dedicated field
|
||||
/// because we expect to be able to satisfy most requests from it.
|
||||
/// </summary>
|
||||
private T? firstItem;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ObjectPool{T}"/> instance with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="factory">The input factory to produce <typeparamref name="T"/> items.</param>
|
||||
public ObjectPool(Func<T> factory)
|
||||
: this(factory, Environment.ProcessorCount * 2)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ObjectPool{T}"/> instance with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="factory">The input factory to produce <typeparamref name="T"/> items.</param>
|
||||
/// <param name="size">The pool size to use.</param>
|
||||
public ObjectPool(Func<T> factory, int size)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.items = new Element[size - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces a <typeparamref name="T"/> instance.
|
||||
/// </summary>
|
||||
/// <returns>The returned <typeparamref name="T"/> item to use.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Allocate()
|
||||
{
|
||||
T? item = this.firstItem;
|
||||
|
||||
if (item is null || item != Interlocked.CompareExchange(ref this.firstItem, null, item))
|
||||
{
|
||||
item = AllocateSlow();
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a given <typeparamref name="T"/> instance to the pool.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <typeparamref name="T"/> instance to return.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Free(T obj)
|
||||
{
|
||||
if (this.firstItem is null)
|
||||
{
|
||||
this.firstItem = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeSlow(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new <typeparamref name="T"/> item.
|
||||
/// </summary>
|
||||
/// <returns>The returned <typeparamref name="T"/> item to use.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private T AllocateSlow()
|
||||
{
|
||||
foreach (ref Element element in this.items.AsSpan())
|
||||
{
|
||||
T? instance = element.Value;
|
||||
|
||||
if (instance is not null)
|
||||
{
|
||||
if (instance == Interlocked.CompareExchange(ref element.Value, null, instance))
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.factory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees a given <typeparamref name="T"/> item.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <typeparamref name="T"/> item to return to the pool.</param>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void FreeSlow(T obj)
|
||||
{
|
||||
foreach (ref Element element in this.items.AsSpan())
|
||||
{
|
||||
if (element.Value is null)
|
||||
{
|
||||
element.Value = obj;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A container for a produced item (using a <see langword="struct"/> wrapper to avoid covariance checks).
|
||||
/// </summary>
|
||||
private struct Element
|
||||
{
|
||||
/// <summary>
|
||||
/// The value held at the current element.
|
||||
/// </summary>
|
||||
internal T? Value;
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче