| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using Avalonia.Platform;
- using Avalonia.Threading;
- // Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
- 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;
- bool _reclaimImmediately;
- 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);
- if (AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>() == null
- && AvaloniaLocator.Current.GetService<IDispatcherImpl>() == null)
- _reclaimImmediately = true;
- else
- 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 && !_reclaimImmediately)
- {
- _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);
- }
|