committed by
GitHub
14 changed files with 412 additions and 108 deletions
@ -0,0 +1,106 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace Avalonia.Rendering.Composition.Drawing.Nodes; |
||||
|
|
||||
|
internal interface IRenderDataNodePool |
||||
|
{ |
||||
|
void Reduce(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Manages a single cleanup timer shared by all <see cref="RenderDataNodePool{T}"/> instances.
|
||||
|
/// Uses weak references so pools that go out of scope can be garbage collected.
|
||||
|
/// </summary>
|
||||
|
internal static class RenderDataNodePoolCleanup |
||||
|
{ |
||||
|
private static readonly List<WeakReference<IRenderDataNodePool>> s_pools = new(); |
||||
|
private static Timer? s_timer; |
||||
|
|
||||
|
public static void Register(IRenderDataNodePool pool) |
||||
|
{ |
||||
|
lock (s_pools) |
||||
|
{ |
||||
|
s_pools.Add(new WeakReference<IRenderDataNodePool>(pool)); |
||||
|
s_timer ??= new Timer(_ => RunCleanup(), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void RunCleanup() |
||||
|
{ |
||||
|
lock (s_pools) |
||||
|
{ |
||||
|
for (var i = s_pools.Count - 1; i >= 0; i--) |
||||
|
{ |
||||
|
if (s_pools[i].TryGetTarget(out var pool)) |
||||
|
pool.Reduce(); |
||||
|
else |
||||
|
s_pools.RemoveAt(i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Object pool for render data nodes with gradual reclamation during idle periods.
|
||||
|
/// While the pool is actively used, items are retained for reuse. Once activity stops,
|
||||
|
/// a shared timer gradually releases a third of pooled items per cycle until empty.
|
||||
|
/// </summary>
|
||||
|
internal sealed class RenderDataNodePool<T> : IRenderDataNodePool where T : class, new() |
||||
|
{ |
||||
|
private T[] _items = Array.Empty<T>(); |
||||
|
private int _count; |
||||
|
private bool _active; |
||||
|
|
||||
|
public RenderDataNodePool() |
||||
|
{ |
||||
|
RenderDataNodePoolCleanup.Register(this); |
||||
|
} |
||||
|
|
||||
|
public T Get() |
||||
|
{ |
||||
|
lock (_items) |
||||
|
{ |
||||
|
_active = true; |
||||
|
|
||||
|
if (_count > 0) |
||||
|
return _items[--_count]; |
||||
|
} |
||||
|
|
||||
|
return new T(); |
||||
|
} |
||||
|
|
||||
|
public void Return(T item) |
||||
|
{ |
||||
|
lock (_items) |
||||
|
{ |
||||
|
_active = true; |
||||
|
|
||||
|
if (_count == _items.Length) |
||||
|
Array.Resize(ref _items, Math.Max(4, _items.Length * 2)); |
||||
|
|
||||
|
_items[_count++] = item; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Reduce() |
||||
|
{ |
||||
|
lock (_items) |
||||
|
{ |
||||
|
if (_active) |
||||
|
{ |
||||
|
_active = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (_count == 0) |
||||
|
return; |
||||
|
|
||||
|
var release = Math.Max(1, _count / 3); |
||||
|
var newCount = _count - release; |
||||
|
Array.Clear(_items, newCount, release); |
||||
|
_count = newCount; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue