using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace CeresSharp.Native;
///
/// Helper class for managing GCHandle resources and ensuring proper cleanup.
/// Provides utilities for tracking and disposing of pinned handles.
///
internal sealed class ResourceManager : IDisposable
{
private readonly List _handles = new List();
private bool _disposed;
///
/// Adds a GCHandle to be managed and disposed.
///
/// The GCHandle to track.
/// Thrown when the manager is disposed.
public void AddHandle(GCHandle handle)
{
if (_disposed)
throw new ObjectDisposedException(nameof(ResourceManager));
lock (_handles)
{
_handles.Add(handle);
}
}
///
/// Adds multiple GCHandles to be managed and disposed.
///
/// The GCHandles to track.
/// Thrown when the manager is disposed.
public void AddHandles(IEnumerable handles)
{
if (_disposed)
throw new ObjectDisposedException(nameof(ResourceManager));
lock (_handles)
{
_handles.AddRange(handles);
}
}
///
/// Pins an array and adds the handle to the manager.
///
/// The unmanaged type of the array elements.
/// The array to pin.
/// The pinned GCHandle.
/// Thrown when array is null.
/// Thrown when the manager is disposed.
public GCHandle PinArray(T[] array) where T : unmanaged
{
if (array == null)
throw new ArgumentNullException(nameof(array));
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
AddHandle(handle);
return handle;
}
///
/// Gets all handles as an array (for safe enumeration during disposal).
///
/// Array of GCHandles.
public GCHandle[] GetHandlesSnapshot()
{
lock (_handles)
{
return _handles.ToArray();
}
}
///
/// Clears all handles without disposing them.
/// Useful when ownership is transferred to another object.
///
public void Clear()
{
lock (_handles)
{
_handles.Clear();
}
}
public void Dispose()
{
if (!_disposed)
{
// Get snapshot to avoid issues during enumeration
var handlesToFree = GetHandlesSnapshot();
lock (_handles)
{
_handles.Clear();
}
// Free handles outside the lock
foreach (var handle in handlesToFree)
{
try
{
if (handle.IsAllocated)
handle.Free();
}
catch
{
// Ignore errors during cleanup - handles may already be freed
}
}
_disposed = true;
}
}
}