From 6180dba837e3694ddcdde86cbc0057af11577a26 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Mon, 11 May 2020 17:53:38 +0100 Subject: [PATCH 1/6] Drafting out fix --- src/Bugsnag/Bugsnag.csproj | 4 +++- src/Bugsnag/UnhandledException.cs | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Bugsnag/Bugsnag.csproj b/src/Bugsnag/Bugsnag.csproj index c3f9b7a..509d649 100644 --- a/src/Bugsnag/Bugsnag.csproj +++ b/src/Bugsnag/Bugsnag.csproj @@ -1,4 +1,4 @@ - + Bugsnag Bugsnag .NET Notifier @@ -19,6 +19,8 @@ + + diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index a36452f..ca7b2b7 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,9 +11,11 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; + private bool _unobservedTerminates; private UnhandledException() { + _unobservedTerminates = DetermineUnobservedTerminates(); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -46,9 +48,33 @@ public void ConfigureClient(IClient client, IConfiguration configuration) } } + /// + /// Determines if an UnobservedTaskException leads to the process terminating, based on the target + /// framework and (when applicable) configuration. + /// + /// + private bool DetermineUnobservedTerminates() + { +#if NET35 || NET40 + return true; +#elif NET45 + System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); + if (configFile != null) + { + // TODO Null check all the way down + Console.WriteLine(configFile.Element("runtime").Element("ThrowUnobservedTaskExceptions").Attribute("enabled").Value); + } +#elif NETSTANDARD1_3 || NETSTANDARD2_0 + // TODO SetupInformation does not exist in .NET Standard 1.3 + // ConfigurationMAnager _might_ be an option in .NET Standard 2.0 + //AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName + return false; +#endif + } + private void CurrentDomain_ProcessExit(object sender, EventArgs e) { - HandleEvent(null, true); + HandleEvent(null, _unobservedTerminates); } private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) From f4703ee901a05e4e26b3c18aa206d7a34cee45b4 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Mon, 11 May 2020 18:05:28 +0100 Subject: [PATCH 2/6] Pass flag from correct call. --- src/Bugsnag/UnhandledException.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index ca7b2b7..2ca80b8 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -74,12 +74,12 @@ private bool DetermineUnobservedTerminates() private void CurrentDomain_ProcessExit(object sender, EventArgs e) { - HandleEvent(null, _unobservedTerminates); + HandleEvent(null, true); } private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { - HandleEvent(e.Exception as Exception, !e.Observed); + HandleEvent(e.Exception as Exception, _unobservedTerminates); } [HandleProcessCorruptedStateExceptions] From 6b9af4fbc75040b65cc9792164cf7e7ad2fb7599 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 10:03:36 +0100 Subject: [PATCH 3/6] Reworked for .NET Framework targets --- src/Bugsnag/UnhandledException.cs | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 2ca80b8..7a7c241 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,11 +11,15 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; +#if !(NET35 || NET40) private bool _unobservedTerminates; +#endif private UnhandledException() { +#if !(NET35 || NET40) _unobservedTerminates = DetermineUnobservedTerminates(); +#endif AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -53,24 +57,25 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// framework and (when applicable) configuration. /// /// +#if NET45 private bool DetermineUnobservedTerminates() { -#if NET35 || NET40 - return true; -#elif NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); - if (configFile != null) - { - // TODO Null check all the way down - Console.WriteLine(configFile.Element("runtime").Element("ThrowUnobservedTaskExceptions").Attribute("enabled").Value); - } -#elif NETSTANDARD1_3 || NETSTANDARD2_0 - // TODO SetupInformation does not exist in .NET Standard 1.3 - // ConfigurationMAnager _might_ be an option in .NET Standard 2.0 - //AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName + var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; + bool value; + var success = bool.TryParse(configValue, out value); + return success && value; + } +#endif + +#if NETSTANDARD1_3 || NETSTANDARD2_0 + private bool DetermineUnobservedTerminates() + { + //Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile); + return false; + } #endif - } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { @@ -79,7 +84,11 @@ private void CurrentDomain_ProcessExit(object sender, EventArgs e) private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { +#if NET35 || NET40 + HandleEvent(e.Exception as Exception, !e.Observed); +#else HandleEvent(e.Exception as Exception, _unobservedTerminates); +#endif } [HandleProcessCorruptedStateExceptions] From 616837c91c6dc88cc87c0b498899ec3637ca74ae Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 12:17:27 +0100 Subject: [PATCH 4/6] Hard code .NET Standard to non-terminating --- src/Bugsnag/UnhandledException.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 7a7c241..947eab4 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -57,25 +57,18 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// framework and (when applicable) configuration. /// /// -#if NET45 private bool DetermineUnobservedTerminates() { +#if NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; bool value; var success = bool.TryParse(configValue, out value); return success && value; - } -#endif - -#if NETSTANDARD1_3 || NETSTANDARD2_0 - private bool DetermineUnobservedTerminates() - { - //Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile); - +#else //NETSTANDARD1_3 || NETSTANDARD2_0 return false; - } #endif + } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { From 064d9a84e5c028e28cea1f9f8f58ffa862a74375 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 13:37:55 +0100 Subject: [PATCH 5/6] CHANEGLOG updated --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff90e1e..22969fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,14 @@ Changelog ### Bug fixes -* Handle any exceptions raised when reading files for code segments +* Handle any exceptions raised when reading files for code segments. | [twometresteve](https://github.com/twometresteve) | [#123](https://github.com/bugsnag/bugsnag-dotnet/pull/123) +* Account for process termination behavior when handling UnobservedTaskExceptions. + | [twometresteve](https://github.com/twometresteve) + | [#125](https://github.com/bugsnag/bugsnag-dotnet/pull/125) + ## 2.2.0 (2018-07-19) ### Enhancements From 9915f93a3a0240ca44d0651811e894f50038f02e Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Wed, 13 May 2020 16:39:38 +0100 Subject: [PATCH 6/6] Conditional compilation logic simplified --- src/Bugsnag/UnhandledException.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 947eab4..d884f29 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,15 +11,11 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; -#if !(NET35 || NET40) private bool _unobservedTerminates; -#endif private UnhandledException() { -#if !(NET35 || NET40) _unobservedTerminates = DetermineUnobservedTerminates(); -#endif AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -59,7 +55,9 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// private bool DetermineUnobservedTerminates() { -#if NET45 +#if NET35 || NET40 + return true; +#elif NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; bool value; @@ -77,11 +75,7 @@ private void CurrentDomain_ProcessExit(object sender, EventArgs e) private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { -#if NET35 || NET40 - HandleEvent(e.Exception as Exception, !e.Observed); -#else - HandleEvent(e.Exception as Exception, _unobservedTerminates); -#endif + HandleEvent(e.Exception as Exception, _unobservedTerminates && !e.Observed); } [HandleProcessCorruptedStateExceptions]