Browse Source

Simplify some runtime platform interfaces and prefer net6 alternatives

pull/8183/head
Max Katz 4 years ago
parent
commit
8e4d904a5a
  1. 5
      src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
  2. 56
      src/Avalonia.PlatformSupport/DynLoader.cs
  3. 4
      src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs
  4. 146
      src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs
  5. 11
      src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs

5
src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs

@ -16,5 +16,10 @@ namespace Avalonia.Platform.Interop
{
}
public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

56
src/Avalonia.PlatformSupport/DynLoader.cs

@ -26,25 +26,6 @@ namespace Avalonia.PlatformSupport
}
}
static class AndroidImports
{
[DllImport("libdl.so")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("libdl.so")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so")]
private static extern IntPtr dlerror();
public static void Init()
{
DlOpen = dlopen;
DlSym = dlsym;
DlError = dlerror;
}
}
static class OsXImports
{
[DllImport("/usr/lib/libSystem.dylib")]
@ -77,10 +58,6 @@ namespace Avalonia.PlatformSupport
Marshal.FreeHGlobal(buffer);
if (unixName == "Darwin")
OsXImports.Init();
#if NET6_0_OR_GREATER
else if (OperatingSystem.IsAndroid())
AndroidImports.Init();
#endif
else
LinuxImports.Init();
}
@ -135,6 +112,39 @@ namespace Avalonia.PlatformSupport
return ptr;
}
}
#if NET6_0_OR_GREATER
internal class Net6Loader : IDynamicLibraryLoader
{
public IntPtr LoadLibrary(string dll)
{
try
{
return NativeLibrary.Load(dll);
}
catch (Exception ex)
{
throw new DynamicLibraryLoaderException("Error loading " + dll, ex);
}
}
public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
{
try
{
if (optional)
{
return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default;
}
return NativeLibrary.GetExport(dll, proc);
}
catch (Exception ex)
{
throw new DynamicLibraryLoaderException("Error " + dll, ex);
}
}
}
#endif
internal class NotSupportedLoader : IDynamicLibraryLoader
{

4
src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Avalonia.PlatformSupport.Internal;
@ -29,9 +30,8 @@ internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver
}
else
{
// iOS does not support loading assemblies dynamically!
#if NET6_0_OR_GREATER
if (OperatingSystem.IsIOS())
if (!RuntimeFeature.IsDynamicCodeSupported)
{
throw new InvalidOperationException(
$"Assembly {name} needs to be referenced and explicitly loaded before loading resources");

146
src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Platform;
@ -14,45 +12,20 @@ namespace Avalonia.PlatformSupport
return new Timer(_ => tick(), null, interval, interval);
}
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size);
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size);
private class UnmanagedBlob : IUnmanagedBlob
{
private readonly StandardRuntimePlatform _plat;
private IntPtr _address;
private readonly object _lock = new object();
#if DEBUG
private static readonly List<string> Backtraces = new List<string>();
private static Thread? GCThread;
private readonly string _backtrace;
private static readonly object _btlock = new object();
class GCThreadDetector
{
~GCThreadDetector()
{
GCThread = Thread.CurrentThread;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void Spawn() => new GCThreadDetector();
static UnmanagedBlob()
{
Spawn();
GC.WaitForPendingFinalizers();
}
#endif
public UnmanagedBlob(StandardRuntimePlatform plat, int size)
public UnmanagedBlob(int size)
{
try
{
if (size <= 0)
throw new ArgumentException("Positive number required", nameof(size));
_plat = plat;
_address = plat.Alloc(size);
_address = Marshal.AllocHGlobal(size);
GC.AddMemoryPressure(size);
Size = size;
}
@ -61,24 +34,15 @@ namespace Avalonia.PlatformSupport
GC.SuppressFinalize(this);
throw;
}
#if DEBUG
_backtrace = Environment.StackTrace;
lock (_btlock)
Backtraces.Add(_backtrace);
#endif
}
void DoDispose()
private void DoDispose()
{
lock (_lock)
{
if (!IsDisposed)
{
#if DEBUG
lock (_btlock)
Backtraces.Remove(_backtrace);
#endif
_plat?.Free(_address, Size);
Marshal.FreeHGlobal(_address);
GC.RemoveMemoryPressure(Size);
IsDisposed = true;
_address = IntPtr.Zero;
@ -89,29 +53,12 @@ namespace Avalonia.PlatformSupport
public void Dispose()
{
#if DEBUG
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
{
lock (_lock)
{
if (!IsDisposed)
{
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: "
+ Environment.StackTrace
+ "\n\nBlob created by " + _backtrace);
}
}
}
#endif
DoDispose();
GC.SuppressFinalize(this);
}
~UnmanagedBlob()
{
#if DEBUG
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace);
#endif
DoDispose();
}
@ -119,82 +66,25 @@ namespace Avalonia.PlatformSupport
public int Size { get; private set; }
public bool IsDisposed { get; private set; }
}
#if NET461 || NETCOREAPP2_0_OR_GREATER
[DllImport("libc", SetLastError = true)]
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset);
[DllImport("libc", SetLastError = true)]
private static extern int munmap(IntPtr addr, IntPtr length);
[DllImport("libc", SetLastError = true)]
private static extern long sysconf(int name);
private bool? _useMmap;
private bool UseMmap
=> _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value;
IntPtr Alloc(int size)
{
if (UseMmap)
{
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero);
if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff)
{
var errno = Marshal.GetLastWin32Error();
throw new Exception("Unable to allocate memory: " + errno);
}
return rv;
}
else
return Marshal.AllocHGlobal(size);
}
void Free(IntPtr ptr, int len)
{
if (UseMmap)
{
if (munmap(ptr, new IntPtr(len)) == -1)
{
var errno = Marshal.GetLastWin32Error();
throw new Exception("Unable to free memory: " + errno);
}
}
else
Marshal.FreeHGlobal(ptr);
}
#else
IntPtr Alloc(int size) => Marshal.AllocHGlobal(size);
void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr);
#endif
private static readonly Lazy<RuntimePlatformInfo> Info = new Lazy<RuntimePlatformInfo>(() =>
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
{
OperatingSystemType os;
#if NET5_0_OR_GREATER
if (OperatingSystem.IsWindows())
os = OperatingSystemType.WinNT;
else if (OperatingSystem.IsMacOS())
os = OperatingSystemType.OSX;
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
os = OperatingSystemType.Linux;
else if (OperatingSystem.IsAndroid())
os = OperatingSystemType.Android;
else if (OperatingSystem.IsIOS())
os = OperatingSystemType.iOS;
else if (OperatingSystem.IsBrowser())
os = OperatingSystemType.Browser;
else
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription);
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
os = OperatingSystemType.OSX;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")))
os = OperatingSystemType.Linux;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
os = OperatingSystemType.WinNT;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android")))
os = OperatingSystemType.Android;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS")))
os = OperatingSystemType.iOS;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser")))
os = OperatingSystemType.Browser;
else
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription);
#endif
return new RuntimePlatformInfo
{
@ -203,10 +93,10 @@ namespace Avalonia.PlatformSupport
#elif NETFRAMEWORK
IsDotNetFramework = true,
#endif
IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT,
IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser,
IsMobile = os == OperatingSystemType.Android || os == OperatingSystemType.iOS,
IsUnix = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.Android,
IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT,
IsMono = os is OperatingSystemType.Android or OperatingSystemType.iOS or OperatingSystemType.Browser,
IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS,
IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android,
IsBrowser = os == OperatingSystemType.Browser,
OperatingSystem = os,
};

11
src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs

@ -9,22 +9,25 @@ namespace Avalonia.PlatformSupport
public static void Register(Assembly? assembly = null)
{
var standardPlatform = new StandardRuntimePlatform();
var os = standardPlatform.GetRuntimeInfo().OperatingSystem;
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToConstant(standardPlatform)
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly))
.Bind<IDynamicLibraryLoader>().ToConstant(
os switch
#if NET6_0_OR_GREATER
new Net6Loader()
#else
standardPlatform.GetRuntimeInfo().OperatingSystem switch
{
OperatingSystemType.WinNT => new Win32Loader(),
OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(),
OperatingSystemType.OSX => new UnixLoader(),
OperatingSystemType.Linux => new UnixLoader(),
OperatingSystemType.Android => new UnixLoader(),
// iOS, WASM, ...
_ => (IDynamicLibraryLoader)new NotSupportedLoader()
_ => new NotSupportedLoader()
}
#endif
);
}
}

Loading…
Cancel
Save