121 lines
3.5 KiB
C#
121 lines
3.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace CeresSharp.Native;
|
|
|
|
/// <summary>
|
|
/// Helper class for managing GCHandle resources and ensuring proper cleanup.
|
|
/// Provides utilities for tracking and disposing of pinned handles.
|
|
/// </summary>
|
|
internal sealed class ResourceManager : IDisposable
|
|
{
|
|
private readonly List<GCHandle> _handles = new List<GCHandle>();
|
|
private bool _disposed;
|
|
|
|
/// <summary>
|
|
/// Adds a GCHandle to be managed and disposed.
|
|
/// </summary>
|
|
/// <param name="handle">The GCHandle to track.</param>
|
|
/// <exception cref="ObjectDisposedException">Thrown when the manager is disposed.</exception>
|
|
public void AddHandle(GCHandle handle)
|
|
{
|
|
if (_disposed)
|
|
throw new ObjectDisposedException(nameof(ResourceManager));
|
|
|
|
lock (_handles)
|
|
{
|
|
_handles.Add(handle);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds multiple GCHandles to be managed and disposed.
|
|
/// </summary>
|
|
/// <param name="handles">The GCHandles to track.</param>
|
|
/// <exception cref="ObjectDisposedException">Thrown when the manager is disposed.</exception>
|
|
public void AddHandles(IEnumerable<GCHandle> handles)
|
|
{
|
|
if (_disposed)
|
|
throw new ObjectDisposedException(nameof(ResourceManager));
|
|
|
|
lock (_handles)
|
|
{
|
|
_handles.AddRange(handles);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pins an array and adds the handle to the manager.
|
|
/// </summary>
|
|
/// <typeparam name="T">The unmanaged type of the array elements.</typeparam>
|
|
/// <param name="array">The array to pin.</param>
|
|
/// <returns>The pinned GCHandle.</returns>
|
|
/// <exception cref="ArgumentNullException">Thrown when array is null.</exception>
|
|
/// <exception cref="ObjectDisposedException">Thrown when the manager is disposed.</exception>
|
|
public GCHandle PinArray<T>(T[] array) where T : unmanaged
|
|
{
|
|
if (array == null)
|
|
throw new ArgumentNullException(nameof(array));
|
|
|
|
var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
|
AddHandle(handle);
|
|
return handle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all handles as an array (for safe enumeration during disposal).
|
|
/// </summary>
|
|
/// <returns>Array of GCHandles.</returns>
|
|
public GCHandle[] GetHandlesSnapshot()
|
|
{
|
|
lock (_handles)
|
|
{
|
|
return _handles.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all handles without disposing them.
|
|
/// Useful when ownership is transferred to another object.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|