From c35755857c14df597d26dafd309fd26f3cfc3273 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 19 Jan 2020 19:35:52 -0300 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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)