From 8e4d904a5af3d71bfdaee77daf2f319961f4b619 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 22:49:09 -0400 Subject: [PATCH] Simplify some runtime platform interfaces and prefer net6 alternatives --- .../Platform/Interop/IDynamicLibraryLoader.cs | 5 + src/Avalonia.PlatformSupport/DynLoader.cs | 56 ++++--- .../Internal/AssemblyDescriptorResolver.cs | 4 +- .../StandardRuntimePlatform.cs | 146 +++--------------- .../StandardRuntimePlatformServices.cs | 11 +- 5 files changed, 65 insertions(+), 157 deletions(-) diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs index 9389ebc703..d9db4b71d5 100644 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs @@ -16,5 +16,10 @@ namespace Avalonia.Platform.Interop { } + + public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException) + { + + } } } diff --git a/src/Avalonia.PlatformSupport/DynLoader.cs b/src/Avalonia.PlatformSupport/DynLoader.cs index ad76ac4724..ef2166d943 100644 --- a/src/Avalonia.PlatformSupport/DynLoader.cs +++ b/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 { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs index 28ae35d57d..6b85200c76 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/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"); diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs b/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs index 4eeb9232cf..048f09570f 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs +++ b/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 Backtraces = new List(); - 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 Info = new Lazy(() => + + private static readonly Lazy 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, }; diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs b/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs index ae7478feb9..11ca906782 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs +++ b/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().ToConstant(standardPlatform) .Bind().ToConstant(new AssetLoader(assembly)) .Bind().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 ); } }