From 19ebf5ad7d8356b9f88716223d1c37759fcb41d6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 23:44:47 -0400 Subject: [PATCH] Bring back blob disposal checks --- .../Platform/Internal/UnmanagedBlob.cs | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index eacf79d4f4..ed0862c06c 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace Avalonia.Platform.Internal; @@ -7,7 +10,30 @@ internal class UnmanagedBlob : IUnmanagedBlob { 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(int size) { try @@ -23,14 +49,23 @@ internal class UnmanagedBlob : IUnmanagedBlob GC.SuppressFinalize(this); throw; } +#if DEBUG + _backtrace = Environment.StackTrace; + lock (_btlock) + Backtraces.Add(_backtrace); +#endif } - - private void DoDispose() + + void DoDispose() { lock (_lock) { if (!IsDisposed) { +#if DEBUG + lock (_btlock) + Backtraces.Remove(_backtrace); +#endif Marshal.FreeHGlobal(_address); GC.RemoveMemoryPressure(Size); IsDisposed = true; @@ -39,18 +74,35 @@ internal class UnmanagedBlob : IUnmanagedBlob } } } - + 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; }