Files
regorus/bindings/csharp/Regorus/ModuleMarshalling.cs
Anand Krishnamoorthi 96360fa9d8 fix(bindings): add SafeHandleWrapper + memory growth checks; bump 0.9.1 (#571)
- Introduce SafeHandleWrapper with gating, short drain wait, and deferred release on last in-flight exit.
- Wire Engine/Program/Rvm/CompiledPolicy to wrapper (centralized handle use, interop helper).
- Add C# memory growth tests (using/finalizer paths) and extend xtask C# runner options.
- Add pooled marshalling utilities, ResultHelpers, and API cleanups; update versions/changelog.

Fixes #570. Closes #554

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2026-02-09 12:22:05 -06:00

157 lines
4.4 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Buffers;
using System.Collections.Generic;
using Regorus;
#nullable enable
namespace Regorus.Internal
{
internal static unsafe class ModuleMarshalling
{
internal sealed class PinnedPolicyModules : IDisposable
{
private readonly List<Utf8Marshaller.PinnedUtf8> _pins;
private bool _disposed;
internal PinnedPolicyModules(RegorusPolicyModule[] buffer, int length, List<Utf8Marshaller.PinnedUtf8> pins)
{
Buffer = buffer;
Length = length;
_pins = pins;
}
internal RegorusPolicyModule[] Buffer { get; }
internal int Length { get; }
public void Dispose()
{
if (_disposed)
{
return;
}
foreach (var pin in _pins)
{
pin.Dispose();
}
ArrayPool<RegorusPolicyModule>.Shared.Return(Buffer, clearArray: true);
_disposed = true;
}
}
internal sealed class PinnedEntryPoints : IDisposable
{
private readonly List<Utf8Marshaller.PinnedUtf8> _pins;
private bool _disposed;
internal PinnedEntryPoints(IntPtr[] buffer, int length, List<Utf8Marshaller.PinnedUtf8> pins)
{
Buffer = buffer;
Length = length;
_pins = pins;
}
internal IntPtr[] Buffer { get; }
internal int Length { get; }
public void Dispose()
{
if (_disposed)
{
return;
}
foreach (var pin in _pins)
{
pin.Dispose();
}
ArrayPool<IntPtr>.Shared.Return(Buffer, clearArray: true);
_disposed = true;
}
}
internal static PinnedPolicyModules PinPolicyModules(IReadOnlyList<PolicyModule> modules)
{
if (modules is null)
{
throw new ArgumentNullException(nameof(modules));
}
var count = modules.Count;
var buffer = ArrayPool<RegorusPolicyModule>.Shared.Rent(count);
var pins = new List<Utf8Marshaller.PinnedUtf8>(count * 2);
try
{
for (int i = 0; i < count; i++)
{
var idPinned = Utf8Marshaller.Pin(modules[i].Id);
var contentPinned = Utf8Marshaller.Pin(modules[i].Content);
pins.Add(idPinned);
pins.Add(contentPinned);
buffer[i] = new RegorusPolicyModule
{
id = idPinned.Pointer,
content = contentPinned.Pointer
};
}
return new PinnedPolicyModules(buffer, count, pins);
}
catch
{
foreach (var pin in pins)
{
pin.Dispose();
}
ArrayPool<RegorusPolicyModule>.Shared.Return(buffer, clearArray: true);
throw;
}
}
internal static PinnedEntryPoints PinEntryPoints(IReadOnlyList<string> entryPoints)
{
if (entryPoints is null)
{
throw new ArgumentNullException(nameof(entryPoints));
}
var count = entryPoints.Count;
var buffer = ArrayPool<IntPtr>.Shared.Rent(count);
var pins = new List<Utf8Marshaller.PinnedUtf8>(count);
try
{
for (int i = 0; i < count; i++)
{
var entryPinned = Utf8Marshaller.Pin(entryPoints[i]);
pins.Add(entryPinned);
buffer[i] = (IntPtr)entryPinned.Pointer;
}
return new PinnedEntryPoints(buffer, count, pins);
}
catch
{
foreach (var pin in pins)
{
pin.Dispose();
}
ArrayPool<IntPtr>.Shared.Return(buffer, clearArray: true);
throw;
}
}
}
}