Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup runtime platform classes #8183

Merged
merged 21 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8e4d904
Simplify some runtime platform interfaces and prefer net6 alternatives
maxkatz6 May 25, 2022
ad0a06b
Merge PlatformSupport project into Avalonia.Base
maxkatz6 May 25, 2022
2538d2c
Copy runtime detection from dotnet/runtime repository
maxkatz6 May 25, 2022
7fee235
Remove Ad-Hoc/AppStore/iPhone/iPhoneSimulator configurations
maxkatz6 May 25, 2022
17a2a4e
Extract UnmanagedBlob to a separated file
maxkatz6 May 25, 2022
6344bca
Merge branch 'master' into cleanup-platformsupport
maxkatz6 May 25, 2022
a907b94
Remove platform support projects
maxkatz6 May 25, 2022
7f90e74
Unwanted namespace changes
maxkatz6 May 25, 2022
b20d563
Merge branch 'cleanup-platformsupport' of https://github.com/Avalonia…
maxkatz6 May 25, 2022
0754e29
Address the review
maxkatz6 May 25, 2022
12fd949
Disable AssemblyName_With_Non_ASCII_Should_Load_Avares test
maxkatz6 May 25, 2022
39d9a01
Fix static SetAssemblyDescriptorResolver usage in tests
maxkatz6 May 25, 2022
2fb68a4
Do not run Avalonia.PlatformSupport.UnitTests tests
maxkatz6 May 25, 2022
5f467a5
Merge branch 'master' into cleanup-platformsupport
maxkatz6 May 26, 2022
19ebf5a
Bring back blob disposal checks
maxkatz6 May 26, 2022
2dd4eb2
Merge branch 'cleanup-platformsupport' of https://github.com/Avalonia…
maxkatz6 May 26, 2022
51789f5
Bring back mmap for linux as discussed
maxkatz6 May 27, 2022
8d6c8cf
Merge branch 'master' into cleanup-platformsupport
maxkatz6 May 30, 2022
0488d0d
Merge branch 'button-memory-leak' into cleanup-platformsupport
maxkatz6 Jun 3, 2022
93b55ef
Merge branch 'master' into cleanup-platformsupport
maxkatz6 Jun 7, 2022
0937887
Fix tests
maxkatz6 Jun 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,490 changes: 13 additions & 1,477 deletions Avalonia.sln

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion build/CoreLibraries.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
</ItemGroup>
</Project>
1 change: 0 additions & 1 deletion nukebuild/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ void RunCoreTest(string projectName)
RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.PlatformSupport.UnitTests");
});

Target RunRenderTests => _ => _
Expand Down
1 change: 0 additions & 1 deletion src/Avalonia.Base/Avalonia.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
<InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.PlatformSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
using System.IO;
using System.Linq;
using System.Reflection;
using Avalonia.Platform;
using Avalonia.PlatformSupport.Internal;
using Avalonia.Platform.Internal;
using Avalonia.Utilities;

namespace Avalonia.PlatformSupport
namespace Avalonia.Platform
{
/// <summary>
/// Loads assets compiled into the application binary.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Reflection;
using Avalonia.Utilities;

namespace Avalonia.PlatformSupport.Internal;
namespace Avalonia.Platform.Internal;

internal interface IAssemblyDescriptor
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Avalonia.PlatformSupport.Internal;
namespace Avalonia.Platform.Internal;

internal interface IAssemblyDescriptorResolver
{
Expand All @@ -29,9 +30,8 @@ public IAssemblyDescriptor GetAssembly(string name)
}
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.IO;
using System.Reflection;

namespace Avalonia.PlatformSupport.Internal;
namespace Avalonia.Platform.Internal;

internal interface IAssetDescriptor
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Avalonia.PlatformSupport.Internal;
namespace Avalonia.Platform.Internal;

internal static class Constants
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Avalonia.Platform.Interop;

// ReSharper disable InconsistentNaming
namespace Avalonia.PlatformSupport
namespace Avalonia.Platform.Internal
{
class UnixLoader : IDynamicLibraryLoader
{
Expand All @@ -26,25 +26,6 @@ public static void Init()
}
}

static class AndroidImports
maxkatz6 marked this conversation as resolved.
Show resolved Hide resolved
{
[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")]
Expand Down Expand Up @@ -77,10 +58,6 @@ static UnixLoader()
Marshal.FreeHGlobal(buffer);
if (unixName == "Darwin")
OsXImports.Init();
#if NET6_0_OR_GREATER
else if (OperatingSystem.IsAndroid())
AndroidImports.Init();
#endif
else
LinuxImports.Init();
}
Expand Down Expand Up @@ -135,6 +112,39 @@ IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool option
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
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.IO;

namespace Avalonia.PlatformSupport.Internal;
namespace Avalonia.Platform.Internal;

internal class SlicedStream : Stream
{
Expand Down
155 changes: 155 additions & 0 deletions src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace Avalonia.Platform.Internal;

internal class UnmanagedBlob : IUnmanagedBlob
{
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(int size)
{
try
{
if (size <= 0)
throw new ArgumentException("Positive number required", nameof(size));
_address = Alloc(size);
GC.AddMemoryPressure(size);
Size = size;
}
catch
{
GC.SuppressFinalize(this);
throw;
}
#if DEBUG
_backtrace = Environment.StackTrace;
lock (_btlock)
Backtraces.Add(_backtrace);
#endif
}

void DoDispose()
{
lock (_lock)
{
if (!IsDisposed)
{
#if DEBUG
lock (_btlock)
Backtraces.Remove(_backtrace);
#endif
Free(_address, Size);
GC.RemoveMemoryPressure(Size);
IsDisposed = true;
_address = IntPtr.Zero;
Size = 0;
}
}
}

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();
}

public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address;
public int Size { get; private set; }
public bool IsDisposed { get; private set; }

[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 = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value);

// Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available.
private IntPtr Alloc(int size)
{
if (!UseMmap)
{
return Marshal.AllocHGlobal(size);
}
else
{
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;
}
}

private void Free(IntPtr ptr, int len)
{
if (!UseMmap)
{
Marshal.FreeHGlobal(ptr);
}
else
{
if (munmap(ptr, new IntPtr(len)) == -1)
{
var errno = Marshal.GetLastWin32Error();
throw new Exception("Unable to free memory: " + errno);
}
}
}
}
5 changes: 5 additions & 0 deletions src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ public DynamicLibraryLoaderException(string message) : base(message)
{

}

public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException)
{

}
}
}
Loading