From c35755857c14df597d26dafd309fd26f3cfc3273 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 19 Jan 2020 19:35:52 -0300 Subject: [PATCH 01/10] basic implementation of exception dispatch on avalonia.native. --- src/Avalonia.Native/Avalonia.Native.csproj | 5 +++-- src/Avalonia.Native/CallbackBase.cs | 10 +++++++++- src/Avalonia.Native/PlatformThreadingInterface.cs | 10 ++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj index 65c2a75b1c..1a2bdeef1e 100644 --- a/src/Avalonia.Native/Avalonia.Native.csproj +++ b/src/Avalonia.Native/Avalonia.Native.csproj @@ -19,8 +19,9 @@ - - + + + diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs index 67c383f6ae..84eff35b30 100644 --- a/src/Avalonia.Native/CallbackBase.cs +++ b/src/Avalonia.Native/CallbackBase.cs @@ -2,11 +2,12 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Runtime.ExceptionServices; using SharpGen.Runtime; namespace Avalonia.Native { - public class CallbackBase : SharpGen.Runtime.IUnknown + public class CallbackBase : SharpGen.Runtime.IUnknown, IExceptionCallback { private uint _refCount; private bool _disposed; @@ -76,5 +77,12 @@ namespace Avalonia.Native { } + + public void RaiseException(Exception e) + { + var exceptionInfo = ExceptionDispatchInfo.Capture(e); + + PlatformThreadingInterface.s_exceptionDispatchInfo = exceptionInfo; + } } } diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index 353124a9d1..000c7a62cf 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Runtime.ExceptionServices; using System.Threading; using Avalonia.Native.Interop; using Avalonia.Platform; @@ -12,6 +13,8 @@ namespace Avalonia.Native { public class PlatformThreadingInterface : IPlatformThreadingInterface { + public static ExceptionDispatchInfo s_exceptionDispatchInfo; + class TimerCallback : CallbackBase, IAvnActionCallback { readonly Action _tick; @@ -82,6 +85,13 @@ namespace Avalonia.Native cancellation = null; } } + + if(s_exceptionDispatchInfo != null) + { + // TODO terminate NSApp. + + s_exceptionDispatchInfo.Throw(); + } } } From 69c0521c5ec115ed16a63362c39c27645edb5fa3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 19 Jan 2020 19:51:36 -0300 Subject: [PATCH 02/10] Add methods for dispatching exceptions on osx. --- src/Avalonia.Native/CallbackBase.cs | 6 ++++-- .../PlatformThreadingInterface.cs | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs index 84eff35b30..0e69bec9cc 100644 --- a/src/Avalonia.Native/CallbackBase.cs +++ b/src/Avalonia.Native/CallbackBase.cs @@ -80,9 +80,11 @@ namespace Avalonia.Native public void RaiseException(Exception e) { - var exceptionInfo = ExceptionDispatchInfo.Capture(e); + var threadingInterface = AvaloniaLocator.Current.GetService(); - PlatformThreadingInterface.s_exceptionDispatchInfo = exceptionInfo; + threadingInterface.TerminateNativeApp(); + + threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e)); } } } diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index 000c7a62cf..ddde4b2837 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -13,8 +13,6 @@ namespace Avalonia.Native { public class PlatformThreadingInterface : IPlatformThreadingInterface { - public static ExceptionDispatchInfo s_exceptionDispatchInfo; - class TimerCallback : CallbackBase, IAvnActionCallback { readonly Action _tick; @@ -46,6 +44,7 @@ namespace Avalonia.Native } readonly IAvnPlatformThreadingInterface _native; + private ExceptionDispatchInfo _exceptionDispatchInfo; public PlatformThreadingInterface(IAvnPlatformThreadingInterface native) { @@ -86,15 +85,23 @@ namespace Avalonia.Native } } - if(s_exceptionDispatchInfo != null) + if(_exceptionDispatchInfo != null) { - // TODO terminate NSApp. - - s_exceptionDispatchInfo.Throw(); + _exceptionDispatchInfo.Throw(); } } } + public void DispatchException (ExceptionDispatchInfo exceptionInfo) + { + _exceptionDispatchInfo = exceptionInfo; + } + + public void TerminateNativeApp() + { + + } + public void Signal(DispatcherPriority priority) { _native.Signal((int)priority); From c3fb1525976c036f8a0fd78362e9ef929cac4e17 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 19 Jan 2020 20:12:56 -0300 Subject: [PATCH 03/10] Working exception dispatch --- native/Avalonia.Native/inc/avalonia-native.h | 1 + native/Avalonia.Native/src/OSX/platformthreading.mm | 6 ++++++ src/Avalonia.Native/CallbackBase.cs | 10 ++++++---- src/Avalonia.Native/PlatformThreadingInterface.cs | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 677fa2f4c5..48822c66ce 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -308,6 +308,7 @@ AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown // Can't pass int* to sharpgentools for some reason virtual void Signal(int priority) = 0; virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; + virtual HRESULT TerminateApp () = 0; }; AVNCOM(IAvnSystemDialogEvents, 0c) : IUnknown diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index e7abedae51..596106c6dc 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -138,6 +138,12 @@ public: return new TimerWrapper(callback, ms); } } + + virtual HRESULT TerminateApp () override + { + [NSApp stop:0]; + return S_OK; + } }; @implementation Signaler diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs index 0e69bec9cc..1356dd58ff 100644 --- a/src/Avalonia.Native/CallbackBase.cs +++ b/src/Avalonia.Native/CallbackBase.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.ExceptionServices; using SharpGen.Runtime; +using Avalonia.Platform; namespace Avalonia.Native { @@ -80,11 +81,12 @@ namespace Avalonia.Native public void RaiseException(Exception e) { - var threadingInterface = AvaloniaLocator.Current.GetService(); - - threadingInterface.TerminateNativeApp(); + if (AvaloniaLocator.Current.GetService() is PlatformThreadingInterface threadingInterface) + { + threadingInterface.TerminateNativeApp(); - threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e)); + threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e)); + } } } } diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index ddde4b2837..4c70a71772 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -99,7 +99,7 @@ namespace Avalonia.Native public void TerminateNativeApp() { - + _native.TerminateApp(); } public void Signal(DispatcherPriority priority) From 9df55b02d4c8d525dac0c6b9cb991e3f048fa0d4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 09:40:48 -0300 Subject: [PATCH 04/10] Use loopcancellation instead of adding a seperate api. --- native/Avalonia.Native/inc/avalonia-native.h | 1 - .../src/OSX/platformthreading.mm | 6 --- .../PlatformThreadingInterface.cs | 46 +++++++++---------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 48822c66ce..677fa2f4c5 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -308,7 +308,6 @@ AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown // Can't pass int* to sharpgentools for some reason virtual void Signal(int priority) = 0; virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; - virtual HRESULT TerminateApp () = 0; }; AVNCOM(IAvnSystemDialogEvents, 0c) : IUnknown diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index 596106c6dc..e7abedae51 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -138,12 +138,6 @@ public: return new TimerWrapper(callback, ms); } } - - virtual HRESULT TerminateApp () override - { - [NSApp stop:0]; - return S_OK; - } }; @implementation Signaler diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index 4c70a71772..f011648cd2 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -45,6 +45,7 @@ namespace Avalonia.Native readonly IAvnPlatformThreadingInterface _native; private ExceptionDispatchInfo _exceptionDispatchInfo; + private IAvnLoopCancellation _loopCancellation; public PlatformThreadingInterface(IAvnPlatformThreadingInterface native) { @@ -59,36 +60,31 @@ namespace Avalonia.Native public void RunLoop(CancellationToken cancellationToken) { - if (cancellationToken.CanBeCanceled == false) - _native.RunLoop(null); - else + var l = new object(); + _loopCancellation = _native.CreateLoopCancellation(); + cancellationToken.Register(() => { - var l = new object(); - var cancellation = _native.CreateLoopCancellation(); - cancellationToken.Register(() => + lock (l) { - lock (l) - { - cancellation?.Cancel(); - } - }); - try - { - _native.RunLoop(cancellation); + _loopCancellation?.Cancel(); } - finally + }); + try + { + _native.RunLoop(_loopCancellation); + } + finally + { + lock (l) { - lock(l) - { - cancellation?.Dispose(); - cancellation = null; - } + _loopCancellation?.Dispose(); + _loopCancellation = null; } + } - if(_exceptionDispatchInfo != null) - { - _exceptionDispatchInfo.Throw(); - } + if (_exceptionDispatchInfo != null) + { + _exceptionDispatchInfo.Throw(); } } @@ -99,7 +95,7 @@ namespace Avalonia.Native public void TerminateNativeApp() { - _native.TerminateApp(); + _loopCancellation?.Cancel(); } public void Signal(DispatcherPriority priority) From f56a891a0f4b1b9ccbcc75c46c9748d2a40396e6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 10:43:22 -0300 Subject: [PATCH 05/10] prevent possibility of double cancellation of main loop. --- .../PlatformThreadingInterface.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index f011648cd2..5ddaf83ed4 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -45,7 +45,7 @@ namespace Avalonia.Native readonly IAvnPlatformThreadingInterface _native; private ExceptionDispatchInfo _exceptionDispatchInfo; - private IAvnLoopCancellation _loopCancellation; + private CancellationTokenSource _exceptionCancellationSource; public PlatformThreadingInterface(IAvnPlatformThreadingInterface native) { @@ -61,24 +61,30 @@ namespace Avalonia.Native public void RunLoop(CancellationToken cancellationToken) { var l = new object(); - _loopCancellation = _native.CreateLoopCancellation(); - cancellationToken.Register(() => + _exceptionCancellationSource = new CancellationTokenSource(); + + var compositeCancellation = CancellationTokenSource + .CreateLinkedTokenSource(cancellationToken, _exceptionCancellationSource.Token).Token; + + var cancellation = _native.CreateLoopCancellation(); + compositeCancellation.Register(() => { lock (l) { - _loopCancellation?.Cancel(); + cancellation?.Cancel(); } }); + try { - _native.RunLoop(_loopCancellation); + _native.RunLoop(cancellation); } finally { lock (l) { - _loopCancellation?.Dispose(); - _loopCancellation = null; + cancellation?.Dispose(); + cancellation = null; } } @@ -95,7 +101,7 @@ namespace Avalonia.Native public void TerminateNativeApp() { - _loopCancellation?.Cancel(); + _exceptionCancellationSource?.Cancel(); } public void Signal(DispatcherPriority priority) From d49109877c887416f146a09d9c83d0bbae3e4abe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 11:47:04 -0300 Subject: [PATCH 06/10] X11 backend wont crash with 0 sized window. --- .../PlatformSupport/StandardRuntimePlatform.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs index 8a5a725594..499f263a9a 100644 --- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs @@ -52,11 +52,13 @@ namespace Avalonia.Shared.PlatformSupport public UnmanagedBlob(StandardRuntimePlatform plat, int size) { - if (size <= 0) - throw new ArgumentException("Positive number required", nameof(size)); - _plat = plat; - _address = plat.Alloc(size); - GC.AddMemoryPressure(size); + if (size > 0) + { + _plat = plat; + _address = plat.Alloc(size); + GC.AddMemoryPressure(size); + } + Size = size; #if DEBUG _backtrace = Environment.StackTrace; @@ -75,7 +77,7 @@ namespace Avalonia.Shared.PlatformSupport lock (_btlock) Backtraces.Remove(_backtrace); #endif - _plat.Free(_address, Size); + _plat?.Free(_address, Size); GC.RemoveMemoryPressure(Size); IsDisposed = true; _address = IntPtr.Zero; From 9fc7842eefcce5ad24300f27eb76f4eff7efa4b4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 11:52:33 -0300 Subject: [PATCH 07/10] Revert "X11 backend wont crash with 0 sized window." This reverts commit d49109877c887416f146a09d9c83d0bbae3e4abe. --- .../PlatformSupport/StandardRuntimePlatform.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs index 499f263a9a..8a5a725594 100644 --- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs @@ -52,13 +52,11 @@ namespace Avalonia.Shared.PlatformSupport public UnmanagedBlob(StandardRuntimePlatform plat, int size) { - if (size > 0) - { - _plat = plat; - _address = plat.Alloc(size); - GC.AddMemoryPressure(size); - } - + if (size <= 0) + throw new ArgumentException("Positive number required", nameof(size)); + _plat = plat; + _address = plat.Alloc(size); + GC.AddMemoryPressure(size); Size = size; #if DEBUG _backtrace = Environment.StackTrace; @@ -77,7 +75,7 @@ namespace Avalonia.Shared.PlatformSupport lock (_btlock) Backtraces.Remove(_backtrace); #endif - _plat?.Free(_address, Size); + _plat.Free(_address, Size); GC.RemoveMemoryPressure(Size); IsDisposed = true; _address = IntPtr.Zero; From 1f1d3a8e35ccc02889a28477ce1e3320d4146707 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 11:53:26 -0300 Subject: [PATCH 08/10] prevent crashing in finalizer --- src/Shared/PlatformSupport/StandardRuntimePlatform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs index 8a5a725594..a23c31ca64 100644 --- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs @@ -75,7 +75,7 @@ namespace Avalonia.Shared.PlatformSupport lock (_btlock) Backtraces.Remove(_backtrace); #endif - _plat.Free(_address, Size); + _plat?.Free(_address, Size); GC.RemoveMemoryPressure(Size); IsDisposed = true; _address = IntPtr.Zero; From 32df775398682e31ad2dafc9ccb95836e3622ad3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Jan 2020 12:06:31 -0300 Subject: [PATCH 09/10] Add hack to prevent renderer crashing on 0x0 window x11. --- src/Avalonia.X11/X11Framebuffer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 00288f300d..94f930e9ec 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -14,6 +14,10 @@ namespace Avalonia.X11 public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int height, double factor) { + // HACK! Please fix renderer, should never ask for 0x0 bitmap. + width = Math.Max(1, width); + height = Math.Max(1, height); + _display = display; _xid = xid; _depth = depth; From 7e42523008752f4c86b8b8f5bcb995168d7ec077 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 20 Jan 2020 23:27:00 +0800 Subject: [PATCH 10/10] Additional sanity checks for monitor resolution --- src/Avalonia.X11/X11Window.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 6ff5b96f12..919abae243 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -102,7 +102,7 @@ namespace Avalonia.X11 valueMask |= SetWindowValuemask.ColorMap; } - int defaultWidth = 300, defaultHeight = 200; + int defaultWidth = 0, defaultHeight = 0; if (!_popup && Screen != null) { @@ -117,6 +117,10 @@ namespace Avalonia.X11 } } + // check if the calculated size is zero then compensate to hardcoded resolution + defaultWidth = Math.Max(defaultWidth, 300); + defaultHeight = Math.Max(defaultHeight, 200); + _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, defaultWidth, defaultHeight, 0, depth, (int)CreateWindowArgs.InputOutput, @@ -133,7 +137,7 @@ namespace Avalonia.X11 _renderHandle = _handle; Handle = new PlatformHandle(_handle, "XID"); - _realSize = new PixelSize(300, 200); + _realSize = new PixelSize(defaultWidth, defaultHeight); platform.Windows[_handle] = OnEvent; XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask