From 0786eb15f1c707345ea10af32e04af6c96ab3bcf Mon Sep 17 00:00:00 2001
From: midare160 <43279447+midare160@users.noreply.github.com>
Date: Sun, 28 Jun 2026 20:11:56 +0200
Subject: [PATCH 1/4] Start of fixing multiple calls to Dispose()
From 45f9a48208125dddfe200ea2fbb9605c80935b0a Mon Sep 17 00:00:00 2001
From: midare160 <43279447+midare160@users.noreply.github.com>
Date: Sun, 28 Jun 2026 21:37:27 +0200
Subject: [PATCH 2/4] Fix multiple disposal of UnmanagedLibrary
---
.../Silk.NET.Core/Loader/UnmanagedLibrary.cs | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/Core/Silk.NET.Core/Loader/UnmanagedLibrary.cs b/src/Core/Silk.NET.Core/Loader/UnmanagedLibrary.cs
index 54dc3e96ad..021fd6382f 100644
--- a/src/Core/Silk.NET.Core/Loader/UnmanagedLibrary.cs
+++ b/src/Core/Silk.NET.Core/Loader/UnmanagedLibrary.cs
@@ -15,6 +15,8 @@ public class UnmanagedLibrary : IDisposable
private static readonly LibraryLoader SPlatformDefaultLoader = LibraryLoader.GetPlatformDefaultLoader();
private readonly LibraryLoader _loader;
+ private nint _handle;
+
///
/// Constructs a new NativeLibrary using the platform's default library loader.
///
@@ -55,7 +57,7 @@ public UnmanagedLibrary(string[] names, LibraryLoader loader) : this(names, load
private UnmanagedLibrary(LibraryLoader loader, nint handle)
{
_loader = loader;
- Handle = handle;
+ _handle = handle;
}
///
@@ -82,7 +84,7 @@ public static bool TryCreate(string name, LibraryLoader loader, PathResolver pat
public UnmanagedLibrary(string name, LibraryLoader loader, PathResolver pathResolver)
{
_loader = loader;
- Handle = _loader.LoadNativeLibrary(name, pathResolver);
+ _handle = _loader.LoadNativeLibrary(name, pathResolver);
}
///
@@ -94,20 +96,26 @@ public UnmanagedLibrary(string name, LibraryLoader loader, PathResolver pathReso
public UnmanagedLibrary(string[] names, LibraryLoader loader, PathResolver pathResolver)
{
_loader = loader;
- Handle = _loader.LoadNativeLibrary(names, pathResolver);
+ _handle = _loader.LoadNativeLibrary(names, pathResolver);
}
///
/// The operating system handle of the loaded library.
///
- public nint Handle { get; }
+ public nint Handle => _handle;
///
/// Frees the native library. Function pointers retrieved from this library will be void.
///
public void Dispose()
{
+ if (_handle == 0)
+ {
+ return;
+ }
+
_loader.FreeNativeLibrary(Handle);
+ _handle = 0;
}
///
From 06acb5fd2d70f61b0ca0a114ae0cb9a762cc7786 Mon Sep 17 00:00:00 2001
From: midare160 <43279447+midare160@users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:03:04 +0200
Subject: [PATCH 3/4] Fix disposing of extension breaking parent context
---
src/Core/Silk.NET.Core/Native/NativeAPI.cs | 7 +++++++
src/Core/Silk.NET.Core/Native/NativeApiContainer.cs | 3 +--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/Core/Silk.NET.Core/Native/NativeAPI.cs b/src/Core/Silk.NET.Core/Native/NativeAPI.cs
index 39e86f6d64..bb46c64179 100644
--- a/src/Core/Silk.NET.Core/Native/NativeAPI.cs
+++ b/src/Core/Silk.NET.Core/Native/NativeAPI.cs
@@ -20,6 +20,13 @@ public NativeAPI(INativeContext ctx)
Context = ctx;
}
+ public override void Dispose()
+ {
+ Context.Dispose();
+
+ base.Dispose();
+ }
+
///
/// Whether or not an extension is present. This function might not be valid for some APIs.
///
diff --git a/src/Core/Silk.NET.Core/Native/NativeApiContainer.cs b/src/Core/Silk.NET.Core/Native/NativeApiContainer.cs
index 17f5ad72d6..78b31fa24a 100644
--- a/src/Core/Silk.NET.Core/Native/NativeApiContainer.cs
+++ b/src/Core/Silk.NET.Core/Native/NativeApiContainer.cs
@@ -31,9 +31,8 @@ public NativeApiContainer(INativeContext ctx)
public IVTable CurrentVTable => _vTable;
- public void Dispose()
+ public virtual void Dispose()
{
- _ctx.Dispose();
CurrentVTable.Dispose();
}
From bdd5515be745622f70a24c2789d86e44a43dc152 Mon Sep 17 00:00:00 2001
From: midare160 <43279447+midare160@users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:04:56 +0200
Subject: [PATCH 4/4] Create ExtensionsDisposalTests.cs
---
.../ExtensionsDisposalTests.cs | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 src/OpenAL/Silk.NET.OpenAL.Tests/ExtensionsDisposalTests.cs
diff --git a/src/OpenAL/Silk.NET.OpenAL.Tests/ExtensionsDisposalTests.cs b/src/OpenAL/Silk.NET.OpenAL.Tests/ExtensionsDisposalTests.cs
new file mode 100644
index 0000000000..a9ce80f39a
--- /dev/null
+++ b/src/OpenAL/Silk.NET.OpenAL.Tests/ExtensionsDisposalTests.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Silk.NET.OpenAL.Extensions.Enumeration;
+using Xunit;
+
+namespace Silk.NET.OpenAL.Tests;
+
+public class ExtensionsDisposalTests
+{
+ private static nint GetProcAddress(ALContext alc) =>
+ alc.Context.GetProcAddress("alcIsExtensionPresent");
+
+ [Fact]
+ public unsafe void TestALContextExtensionDispose()
+ {
+ var alc = ALContext.GetApi();
+ alc.TryGetExtension(null, out var ext);
+
+ // Disposing the same object multiple times should not throw
+ ext.Dispose();
+ alc.Dispose();
+
+ alc.Dispose();
+ ext.Dispose();
+ }
+
+ [Fact]
+ public unsafe void TestALContextExtensionDisposeState()
+ {
+ using var alc = ALContext.GetApi();
+ alc.TryGetExtension(null, out var ext);
+
+ var address = GetProcAddress(alc);
+ Assert.NotEqual(0, address);
+
+ ext.Dispose();
+
+ // Disposing the extension should not affect the parent context
+ address = GetProcAddress(alc);
+ Assert.NotEqual(0, address);
+ }
+}