Skip to content

Commit

Permalink
Refactor libary loader and fix mellinoe#34
Browse files Browse the repository at this point in the history
  • Loading branch information
Tacodiva committed Dec 23, 2022
1 parent 395180c commit ee829ee
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 155 deletions.
116 changes: 0 additions & 116 deletions vk/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,120 +60,4 @@ private static Exception CreateMissingFunctionException()
return new InvalidOperationException("The function does not exist or could not be loaded.");
}
}

public abstract class NativeLibrary : IDisposable
{
private readonly string _libraryName;
private readonly IntPtr _libraryHandle;

public IntPtr NativeHandle => _libraryHandle;

public NativeLibrary(string libraryName)
{
_libraryName = libraryName;
_libraryHandle = LoadLibrary(_libraryName);
if (_libraryHandle == IntPtr.Zero)
{
throw new InvalidOperationException("Could not load " + libraryName);
}
}

protected abstract IntPtr LoadLibrary(string libraryName);
protected abstract void FreeLibrary(IntPtr libraryHandle);
protected abstract IntPtr LoadFunction(string functionName);

public IntPtr LoadFunctionPointer(string functionName)
{
if (functionName == null)
{
throw new ArgumentNullException(nameof(functionName));
}

return LoadFunction(functionName);
}

public void Dispose()
{
FreeLibrary(_libraryHandle);
}


public static NativeLibrary Load(string libraryName)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new WindowsNativeLibrary(libraryName);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
#if NET5_0
|| OperatingSystem.IsAndroid()
#endif
)
{
return new UnixNativeLibrary(libraryName);
}
else
{
throw new PlatformNotSupportedException("Cannot load native libraries on this platform: " + RuntimeInformation.OSDescription);
}
}

private class WindowsNativeLibrary : NativeLibrary
{
public WindowsNativeLibrary(string libraryName) : base(libraryName)
{
}

protected override IntPtr LoadLibrary(string libraryName)
{
return Kernel32.LoadLibrary(libraryName);
}

protected override void FreeLibrary(IntPtr libraryHandle)
{
Kernel32.FreeLibrary(libraryHandle);
}

protected override IntPtr LoadFunction(string functionName)
{
Debug.WriteLine("Loading " + functionName);
return Kernel32.GetProcAddress(NativeHandle, functionName);
}
}

private class UnixNativeLibrary : NativeLibrary
{
public UnixNativeLibrary(string libraryName) : base(libraryName)
{
}

protected override IntPtr LoadLibrary(string libraryName)
{
Libdl.dlerror();
IntPtr handle = Libdl.dlopen(libraryName, Libdl.RTLD_NOW);
if (handle == IntPtr.Zero && !Path.IsPathRooted(libraryName))
{
string baseDir = AppContext.BaseDirectory;
if (!string.IsNullOrWhiteSpace(baseDir))
{
string localPath = Path.Combine(baseDir, libraryName);
handle = Libdl.dlopen(localPath, Libdl.RTLD_NOW);
}
}

return handle;
}

protected override void FreeLibrary(IntPtr libraryHandle)
{
Libdl.dlclose(libraryHandle);
}

protected override IntPtr LoadFunction(string functionName)
{
return Libdl.dlsym(NativeHandle, functionName);
}
}
}
}
17 changes: 0 additions & 17 deletions vk/Kernel32.cs

This file was deleted.

45 changes: 45 additions & 0 deletions vk/LibDLGeneric.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Runtime.InteropServices;

namespace Vulkan
{
internal class NativeLibDLGeneric : NativeLinkerLibDL.INativeLibDL
{
public int dlclose(nint handle)
{
return LibDL.dlclose(handle);
}

public string dlerror()
{
return LibDL.dlerror();
}

public nint dlopen(string fileName, int flags)
{
return LibDL.dlopen(fileName, flags);
}

public nint dlsym(nint handle, string name)
{
return LibDL.dlsym(handle, name);
}

private static class LibDL
{
private const string Library = "libdl";

[DllImport(Library)]
public static extern IntPtr dlopen(string fileName, int flags);

[DllImport(Library)]
public static extern IntPtr dlsym(IntPtr handle, string name);

[DllImport(Library)]
public static extern int dlclose(IntPtr handle);

[DllImport(Library)]
public static extern string dlerror();
}
}
}
22 changes: 0 additions & 22 deletions vk/Libdl.cs

This file was deleted.

45 changes: 45 additions & 0 deletions vk/NativeLibDLLinux.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Runtime.InteropServices;

namespace Vulkan
{
internal class NativeLibDLLinux : NativeLinkerLibDL.INativeLibDL
{
public int dlclose(nint handle)
{
return LibDL.dlclose(handle);
}

public string dlerror()
{
return LibDL.dlerror();
}

public nint dlopen(string fileName, int flags)
{
return LibDL.dlopen(fileName, flags);
}

public nint dlsym(nint handle, string name)
{
return LibDL.dlsym(handle, name);
}

private static class LibDL
{
private const string Library = "libdl.so.2";

[DllImport(Library)]
public static extern IntPtr dlopen(string fileName, int flags);

[DllImport(Library)]
public static extern IntPtr dlsym(IntPtr handle, string name);

[DllImport(Library)]
public static extern int dlclose(IntPtr handle);

[DllImport(Library)]
public static extern string dlerror();
}
}
}
86 changes: 86 additions & 0 deletions vk/NativeLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace Vulkan
{

public interface INativeDynamicLinker
{
public IntPtr LoadLibrary(string libraryName);
public void FreeLibrary(IntPtr libraryHandle);
public IntPtr LoadFunction(IntPtr libraryHandle, string functionName);

private static INativeDynamicLinker _linker;

public static INativeDynamicLinker GetInstance()
{
if (_linker != null) return _linker;

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return _linker = new NativeLinkerWindows();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
#if NET5_0
|| OperatingSystem.IsAndroid()
#endif
)
{
return new NativeLinkerLibDL(new NativeLibDLGeneric());
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return new NativeLinkerLibDL(new NativeLibDLLinux());
}
else
{
throw new PlatformNotSupportedException("Cannot load native libraries on this platform: " + RuntimeInformation.OSDescription);
}
}
}

public class NativeLibrary : IDisposable
{
public static NativeLibrary Load(string libraryName)
{
if (libraryName == null)
throw new ArgumentNullException(nameof(libraryName));

INativeDynamicLinker linker = INativeDynamicLinker.GetInstance();
IntPtr libraryHandle = linker.LoadLibrary(libraryName);

if (libraryHandle == IntPtr.Zero)
throw new InvalidOperationException("Could not load " + libraryName);

return new NativeLibrary(libraryHandle, linker);
}

private readonly IntPtr _libraryHandle;
private readonly INativeDynamicLinker _linker;

public IntPtr NativeHandle => _libraryHandle;

private NativeLibrary(IntPtr libraryHandle, INativeDynamicLinker linker)
{
_libraryHandle = libraryHandle;
_linker = linker;
}

public IntPtr LoadFunctionPointer(string functionName)
{
if (functionName == null)
{
throw new ArgumentNullException(nameof(functionName));
}

return _linker.LoadFunction(_libraryHandle, functionName);
}

public void Dispose()
{
_linker.FreeLibrary(_libraryHandle);
}
}
}
Loading

0 comments on commit ee829ee

Please sign in to comment.