A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

152 lines
3.8 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Avalonia.Threading;
namespace Avalonia.Rendering.Composition.Transport;
/// <summary>
/// A pool that keeps a number of elements that was used in the last 10 seconds
/// </summary>
internal abstract class BatchStreamPoolBase<T> : IDisposable
{
readonly Stack<T> _pool = new();
bool _disposed;
int _usage;
readonly int[] _usageStatistics = new int[10];
int _usageStatisticsSlot;
public int CurrentUsage => _usage;
public int CurrentPool => _pool.Count;
public BatchStreamPoolBase(bool needsFinalize, Action<Func<bool>>? startTimer = null)
{
if(!needsFinalize)
GC.SuppressFinalize(needsFinalize);
var updateRef = new WeakReference<BatchStreamPoolBase<T>>(this);
StartUpdateTimer(startTimer, updateRef);
}
static void StartUpdateTimer(Action<Func<bool>>? startTimer, WeakReference<BatchStreamPoolBase<T>> updateRef)
{
Func<bool> timerProc = () =>
{
if (updateRef.TryGetTarget(out var target))
{
target.UpdateStatistics();
return true;
}
return false;
};
if (startTimer != null)
startTimer(timerProc);
else
DispatcherTimer.Run(timerProc, TimeSpan.FromSeconds(1));
}
private void UpdateStatistics()
{
lock (_pool)
{
var maximumUsage = _usageStatistics.Max();
var recentlyUsedPooledSlots = maximumUsage - _usage;
var keepSlots = Math.Max(recentlyUsedPooledSlots, 10);
while (keepSlots < _pool.Count)
DestroyItem(_pool.Pop());
_usageStatisticsSlot = (_usageStatisticsSlot + 1) % _usageStatistics.Length;
_usageStatistics[_usageStatisticsSlot] = 0;
}
}
protected abstract T CreateItem();
protected virtual void DestroyItem(T item)
{
}
public T Get()
{
lock (_pool)
{
_usage++;
if (_usageStatistics[_usageStatisticsSlot] < _usage)
_usageStatistics[_usageStatisticsSlot] = _usage;
if (_pool.Count != 0)
return _pool.Pop();
}
return CreateItem();
}
public void Return(T item)
{
lock (_pool)
{
_usage--;
if (!_disposed)
{
_pool.Push(item);
return;
}
}
DestroyItem(item);
}
public void Dispose()
{
lock (_pool)
{
_disposed = true;
foreach (var item in _pool)
DestroyItem(item);
_pool.Clear();
}
}
~BatchStreamPoolBase()
{
Dispose();
}
}
internal sealed class BatchStreamObjectPool<T> : BatchStreamPoolBase<T[]> where T : class?
{
public int ArraySize { get; }
public BatchStreamObjectPool(int arraySize = 128, Action<Func<bool>>? startTimer = null) : base(false, startTimer)
{
ArraySize = arraySize;
}
protected override T[] CreateItem()
{
return new T[ArraySize];
}
protected override void DestroyItem(T[] item)
{
Array.Clear(item, 0, item.Length);
}
}
internal sealed class BatchStreamMemoryPool : BatchStreamPoolBase<IntPtr>
{
public int BufferSize { get; }
public BatchStreamMemoryPool(int bufferSize = 1024, Action<Func<bool>>? startTimer = null) : base(true, startTimer)
{
BufferSize = bufferSize;
}
protected override IntPtr CreateItem() => Marshal.AllocHGlobal(BufferSize);
protected override void DestroyItem(IntPtr item) => Marshal.FreeHGlobal(item);
}