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; } } }