// This file is auto-generated by alef. DO NOT EDIT.
#nullable enable
using System;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
{% if has_methods %}
using System.Text.Json;
using System.Text.Json.Serialization;
{% endif %}
{% if needs_list %}
using System.Collections.Generic;
{% endif %}
{% if needs_async %}
using System.Threading.Tasks;
{% endif %}
{% if needs_streaming %}
using System.Threading;
using System.Runtime.CompilerServices;
{% endif %}
namespace {{ namespace }};
// The native handle is the scalar `AlefHandle` (`uint64_t`) registry index, not a pointer.
// `SafeHandle`'s only storage slot is a pointer-width `IntPtr`, so the `ulong` is packed into
// that slot as a raw bit pattern (never dereferenced) to keep `SafeHandle`'s guaranteed
// finalization and `DangerousAddRef`/`DangerousRelease` ref-counting, which the borrow/lease/
// transfer machinery below builds on. This is lossless on every currently supported .NET
// target (IntPtr is 8 bytes on all 64-bit platforms .NET 5+ ships on); on a true 32-bit
// process a handle value above `uint.MaxValue` would truncate, but that requires over four
// billion concurrently live handles of this type, and 32-bit .NET targets are legacy-only. ~keep
internal sealed class {{ class_name }}SafeHandle : SafeHandle
{
internal {{ class_name }}SafeHandle(ulong handle) : base(IntPtr.Zero, true)
{
SetHandle(Pack(handle));
}
public override bool IsInvalid => handle == IntPtr.Zero;
internal static IntPtr Pack(ulong value) => unchecked((IntPtr)(long)value);
internal static ulong Unpack(IntPtr value) => unchecked((ulong)(long)value);
protected override bool ReleaseHandle()
{
NativeMethods.{{ free_method }}(Unpack(handle));
return true;
}
}
{% if doc -%}
/// <summary>
{%- for line in doc_lines %}
/// {{ line }}
{%- endfor %}
/// </summary>
{%- endif %}
public sealed class {{ class_name }} : IDisposable
{
{%- if has_methods %}
private static readonly JsonSerializerOptions JsonOptions = new()
{
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) },
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
private static readonly JsonSerializerOptions JsonSerializationOptions = new()
{
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) },
};
{%- endif %}
private {{ class_name }}SafeHandle _safeHandle;
private readonly object _handleLock = new();
private int _activeBorrows;
private bool _handleUnavailable;
private bool _transferPending;
internal sealed class HandleLease : IDisposable
{
private {{ class_name }}? _owner;
internal HandleLease({{ class_name }} owner, ulong handle)
{
_owner = owner;
Handle = handle;
}
internal ulong Handle { get; }
public void Dispose()
{
var owner = global::System.Threading.Interlocked.Exchange(ref _owner, null);
owner?.ReleaseBorrow();
}
}
private sealed class HandleTransfer : IDisposable
{
private {{ class_name }}? _owner;
private {{ class_name }}SafeHandle? _transferredHandle;
internal HandleTransfer({{ class_name }} owner, ulong handle)
{
_owner = owner;
_transferredHandle = new {{ class_name }}SafeHandle(handle);
}
internal ulong Handle => _transferredHandle is { } transferredHandle
? {{ class_name }}SafeHandle.Unpack(transferredHandle.DangerousGetHandle())
: throw new InvalidOperationException("Handle transfer is no longer active");
internal void Commit()
{
var transferredHandle = _transferredHandle
?? throw new InvalidOperationException("Handle transfer is no longer active");
transferredHandle.SetHandleAsInvalid();
_transferredHandle = null;
var owner = _owner;
_owner = null;
owner?.CommitTransfer();
}
public void Dispose()
{
var transferredHandle = global::System.Threading.Interlocked.Exchange(ref _transferredHandle, null);
var owner = global::System.Threading.Interlocked.Exchange(ref _owner, null);
if (transferredHandle is null) return;
if (owner is null)
{
transferredHandle.Dispose();
return;
}
var handle = {{ class_name }}SafeHandle.Unpack(transferredHandle.DangerousGetHandle());
transferredHandle.SetHandleAsInvalid();
owner.RollbackTransfer(handle);
}
}
internal {{ class_name }}(ulong handle)
{
_safeHandle = new {{ class_name }}SafeHandle(handle);
}
internal ulong Handle => GetHandle();
private ulong GetHandle()
{
lock (_handleLock)
{
ThrowIfHandleUnavailable();
return {{ class_name }}SafeHandle.Unpack(_safeHandle.DangerousGetHandle());
}
}
internal HandleLease BorrowHandle()
{
lock (_handleLock)
{
ThrowIfHandleUnavailable();
var lease = new HandleLease(this, {{ class_name }}SafeHandle.Unpack(_safeHandle.DangerousGetHandle()));
var addRefSucceeded = false;
_safeHandle.DangerousAddRef(ref addRefSucceeded);
if (!addRefSucceeded) throw new ObjectDisposedException(nameof({{ class_name }}));
_activeBorrows++;
return lease;
}
}
private void ReleaseBorrow()
{
lock (_handleLock)
{
_safeHandle.DangerousRelease();
_activeBorrows--;
if (_activeBorrows == 0) global::System.Threading.Monitor.PulseAll(_handleLock);
}
}
private HandleTransfer TakeHandle()
{
lock (_handleLock)
{
ThrowIfHandleUnavailable();
WaitForBorrowedCalls();
var transfer = new HandleTransfer(this, {{ class_name }}SafeHandle.Unpack(_safeHandle.DangerousGetHandle()));
_handleUnavailable = true;
_safeHandle.SetHandleAsInvalid();
_transferPending = true;
return transfer;
}
}
private void RollbackTransfer(ulong handle)
{
lock (_handleLock)
{
_transferPending = false;
_safeHandle = new {{ class_name }}SafeHandle(handle);
_handleUnavailable = false;
global::System.Threading.Monitor.PulseAll(_handleLock);
}
}
private void CommitTransfer()
{
lock (_handleLock)
{
_transferPending = false;
global::System.Threading.Monitor.PulseAll(_handleLock);
}
}
private void WaitForBorrowedCalls()
{
var interrupted = false;
while (_activeBorrows != 0)
{
try
{
global::System.Threading.Monitor.Wait(_handleLock);
}
catch (global::System.Threading.ThreadInterruptedException)
{
interrupted = true;
}
}
if (interrupted) global::System.Threading.Thread.CurrentThread.Interrupt();
}
private void WaitForPendingTransfer()
{
var interrupted = false;
while (_transferPending)
{
try
{
global::System.Threading.Monitor.Wait(_handleLock);
}
catch (global::System.Threading.ThreadInterruptedException)
{
interrupted = true;
}
}
if (interrupted) global::System.Threading.Thread.CurrentThread.Interrupt();
}
private void ThrowIfHandleUnavailable()
{
if (_handleUnavailable || _safeHandle.IsClosed || _safeHandle.IsInvalid)
{
throw new ObjectDisposedException(nameof({{ class_name }}));
}
}
public void Dispose()
{
lock (_handleLock)
{
if (_transferPending)
{
WaitForPendingTransfer();
if (_handleUnavailable) return;
}
if (_handleUnavailable) return;
_handleUnavailable = true;
WaitForBorrowedCalls();
_safeHandle.Dispose();
}
}