Skip to content

Commit 3c4d878

Browse files
authored
Fix exception when using ALContext after disposing an extension (#2596)
* Start of fixing multiple calls to Dispose() * Fix multiple disposal of UnmanagedLibrary * Fix disposing of extension breaking parent context * Create ExtensionsDisposalTests.cs
1 parent d1e0877 commit 3c4d878

4 files changed

Lines changed: 63 additions & 6 deletions

File tree

src/Core/Silk.NET.Core/Loader/UnmanagedLibrary.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public class UnmanagedLibrary : IDisposable
1515
private static readonly LibraryLoader SPlatformDefaultLoader = LibraryLoader.GetPlatformDefaultLoader();
1616
private readonly LibraryLoader _loader;
1717

18+
private nint _handle;
19+
1820
/// <summary>
1921
/// Constructs a new NativeLibrary using the platform's default library loader.
2022
/// </summary>
@@ -55,7 +57,7 @@ public UnmanagedLibrary(string[] names, LibraryLoader loader) : this(names, load
5557
private UnmanagedLibrary(LibraryLoader loader, nint handle)
5658
{
5759
_loader = loader;
58-
Handle = handle;
60+
_handle = handle;
5961
}
6062

6163
/// <summary>
@@ -82,7 +84,7 @@ public static bool TryCreate(string name, LibraryLoader loader, PathResolver pat
8284
public UnmanagedLibrary(string name, LibraryLoader loader, PathResolver pathResolver)
8385
{
8486
_loader = loader;
85-
Handle = _loader.LoadNativeLibrary(name, pathResolver);
87+
_handle = _loader.LoadNativeLibrary(name, pathResolver);
8688
}
8789

8890
/// <summary>
@@ -94,20 +96,26 @@ public UnmanagedLibrary(string name, LibraryLoader loader, PathResolver pathReso
9496
public UnmanagedLibrary(string[] names, LibraryLoader loader, PathResolver pathResolver)
9597
{
9698
_loader = loader;
97-
Handle = _loader.LoadNativeLibrary(names, pathResolver);
99+
_handle = _loader.LoadNativeLibrary(names, pathResolver);
98100
}
99101

100102
/// <summary>
101103
/// The operating system handle of the loaded library.
102104
/// </summary>
103-
public nint Handle { get; }
105+
public nint Handle => _handle;
104106

105107
/// <summary>
106108
/// Frees the native library. Function pointers retrieved from this library will be void.
107109
/// </summary>
108110
public void Dispose()
109111
{
112+
if (_handle == 0)
113+
{
114+
return;
115+
}
116+
110117
_loader.FreeNativeLibrary(Handle);
118+
_handle = 0;
111119
}
112120

113121
/// <summary>

src/Core/Silk.NET.Core/Native/NativeAPI.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ public NativeAPI(INativeContext ctx)
2020
Context = ctx;
2121
}
2222

23+
public override void Dispose()
24+
{
25+
Context.Dispose();
26+
27+
base.Dispose();
28+
}
29+
2330
/// <summary>
2431
/// Whether or not an extension is present. This function might not be valid for some APIs.
2532
/// </summary>

src/Core/Silk.NET.Core/Native/NativeApiContainer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ public NativeApiContainer(INativeContext ctx)
3131

3232
public IVTable CurrentVTable => _vTable;
3333

34-
public void Dispose()
34+
public virtual void Dispose()
3535
{
36-
_ctx.Dispose();
3736
CurrentVTable.Dispose();
3837
}
3938

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Silk.NET.OpenAL.Extensions.Enumeration;
5+
using Xunit;
6+
7+
namespace Silk.NET.OpenAL.Tests;
8+
9+
public class ExtensionsDisposalTests
10+
{
11+
private static nint GetProcAddress(ALContext alc) =>
12+
alc.Context.GetProcAddress("alcIsExtensionPresent");
13+
14+
[Fact]
15+
public unsafe void TestALContextExtensionDispose()
16+
{
17+
var alc = ALContext.GetApi();
18+
alc.TryGetExtension<Enumeration>(null, out var ext);
19+
20+
// Disposing the same object multiple times should not throw
21+
ext.Dispose();
22+
alc.Dispose();
23+
24+
alc.Dispose();
25+
ext.Dispose();
26+
}
27+
28+
[Fact]
29+
public unsafe void TestALContextExtensionDisposeState()
30+
{
31+
using var alc = ALContext.GetApi();
32+
alc.TryGetExtension<Enumeration>(null, out var ext);
33+
34+
var address = GetProcAddress(alc);
35+
Assert.NotEqual(0, address);
36+
37+
ext.Dispose();
38+
39+
// Disposing the extension should not affect the parent context
40+
address = GetProcAddress(alc);
41+
Assert.NotEqual(0, address);
42+
}
43+
}

0 commit comments

Comments
 (0)