Catching exceptions in fire&forget tasks
-
Weekend is approaching, and in case you may feel bored without a complicated programming task, I have a puzzle for you. I already asked at StackOverflow: c# - Logging exceptions in fire&forget tasks - Stack Overflow[^], but did not get a usable answer. In short, I want to create a method which takes an
Action
parameter, fires off aTask
with it, and makes sure that exceptions do not crash the application (but logs the exception messages so that I can find out that something bad happened). I tried following code:public static void CreateAndStartTaskWithErrorLogging(Action \_action, string \_componentName, string \_originalStacktrace = null) { DateTime started = HighPrecisionClock.Now; Task task = new Task(\_action); task.ContinueWith(\_continuation => \_continuation.LogExceptions(\_componentName, started, \_originalStacktrace)); task.ConfigureAwait(false); // tried also without this line task.Start(); } internal static void LogExceptions(this Task \_t, string \_componentName, DateTime \_started, string \_originalStacktrace = null) { try { \_t.Wait(1000); } catch (Exception ex) { Logger.LogError(\_componentName, $"An exception occurred in a fire-and-forget task which was started at {\_started}.\\r\\n" + $"The original stack trace is:\\r\\n{\_originalStacktrace}"); Logger.LogException(\_componentName, ex); } // tried also without this block try { \_t.Dispose(); } catch (Exception dex) { Logger.LogException(\_componentName, dex); } }
Well, normally, it works. Normally. Not always... And then the service crashes with an error entry in the Windows Event Log. (That's also true for the solution in the first answer at SO). Some more background. The application is a highly configurable Windows Service. The function is called with
Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);
, whereDataStore
is set to acomposite
which in turn callsParallel.ForEach(m_I
-
Weekend is approaching, and in case you may feel bored without a complicated programming task, I have a puzzle for you. I already asked at StackOverflow: c# - Logging exceptions in fire&forget tasks - Stack Overflow[^], but did not get a usable answer. In short, I want to create a method which takes an
Action
parameter, fires off aTask
with it, and makes sure that exceptions do not crash the application (but logs the exception messages so that I can find out that something bad happened). I tried following code:public static void CreateAndStartTaskWithErrorLogging(Action \_action, string \_componentName, string \_originalStacktrace = null) { DateTime started = HighPrecisionClock.Now; Task task = new Task(\_action); task.ContinueWith(\_continuation => \_continuation.LogExceptions(\_componentName, started, \_originalStacktrace)); task.ConfigureAwait(false); // tried also without this line task.Start(); } internal static void LogExceptions(this Task \_t, string \_componentName, DateTime \_started, string \_originalStacktrace = null) { try { \_t.Wait(1000); } catch (Exception ex) { Logger.LogError(\_componentName, $"An exception occurred in a fire-and-forget task which was started at {\_started}.\\r\\n" + $"The original stack trace is:\\r\\n{\_originalStacktrace}"); Logger.LogException(\_componentName, ex); } // tried also without this block try { \_t.Dispose(); } catch (Exception dex) { Logger.LogException(\_componentName, dex); } }
Well, normally, it works. Normally. Not always... And then the service crashes with an error entry in the Windows Event Log. (That's also true for the solution in the first answer at SO). Some more background. The application is a highly configurable Windows Service. The function is called with
Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);
, whereDataStore
is set to acomposite
which in turn callsParallel.ForEach(m_I
Thanks for raising the level of discourse :)
«Where is the Life we have lost in living? Where is the wisdom we have lost in knowledge? Where is the knowledge we have lost in information?» T. S. Elliot
-
Weekend is approaching, and in case you may feel bored without a complicated programming task, I have a puzzle for you. I already asked at StackOverflow: c# - Logging exceptions in fire&forget tasks - Stack Overflow[^], but did not get a usable answer. In short, I want to create a method which takes an
Action
parameter, fires off aTask
with it, and makes sure that exceptions do not crash the application (but logs the exception messages so that I can find out that something bad happened). I tried following code:public static void CreateAndStartTaskWithErrorLogging(Action \_action, string \_componentName, string \_originalStacktrace = null) { DateTime started = HighPrecisionClock.Now; Task task = new Task(\_action); task.ContinueWith(\_continuation => \_continuation.LogExceptions(\_componentName, started, \_originalStacktrace)); task.ConfigureAwait(false); // tried also without this line task.Start(); } internal static void LogExceptions(this Task \_t, string \_componentName, DateTime \_started, string \_originalStacktrace = null) { try { \_t.Wait(1000); } catch (Exception ex) { Logger.LogError(\_componentName, $"An exception occurred in a fire-and-forget task which was started at {\_started}.\\r\\n" + $"The original stack trace is:\\r\\n{\_originalStacktrace}"); Logger.LogException(\_componentName, ex); } // tried also without this block try { \_t.Dispose(); } catch (Exception dex) { Logger.LogException(\_componentName, dex); } }
Well, normally, it works. Normally. Not always... And then the service crashes with an error entry in the Windows Event Log. (That's also true for the solution in the first answer at SO). Some more background. The application is a highly configurable Windows Service. The function is called with
Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);
, whereDataStore
is set to acomposite
which in turn callsParallel.ForEach(m_I
Native exceptions thrown by unmanaged code that occured on a thread started by that unmanaged code can't be caught. That's most often originating from a COM-object.
Bernhard Hiller wrote:
the exception may come from non-managed code.
Upvote is for you, as you answered it before me :)
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^] "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
-
Weekend is approaching, and in case you may feel bored without a complicated programming task, I have a puzzle for you. I already asked at StackOverflow: c# - Logging exceptions in fire&forget tasks - Stack Overflow[^], but did not get a usable answer. In short, I want to create a method which takes an
Action
parameter, fires off aTask
with it, and makes sure that exceptions do not crash the application (but logs the exception messages so that I can find out that something bad happened). I tried following code:public static void CreateAndStartTaskWithErrorLogging(Action \_action, string \_componentName, string \_originalStacktrace = null) { DateTime started = HighPrecisionClock.Now; Task task = new Task(\_action); task.ContinueWith(\_continuation => \_continuation.LogExceptions(\_componentName, started, \_originalStacktrace)); task.ConfigureAwait(false); // tried also without this line task.Start(); } internal static void LogExceptions(this Task \_t, string \_componentName, DateTime \_started, string \_originalStacktrace = null) { try { \_t.Wait(1000); } catch (Exception ex) { Logger.LogError(\_componentName, $"An exception occurred in a fire-and-forget task which was started at {\_started}.\\r\\n" + $"The original stack trace is:\\r\\n{\_originalStacktrace}"); Logger.LogException(\_componentName, ex); } // tried also without this block try { \_t.Dispose(); } catch (Exception dex) { Logger.LogException(\_componentName, dex); } }
Well, normally, it works. Normally. Not always... And then the service crashes with an error entry in the Windows Event Log. (That's also true for the solution in the first answer at SO). Some more background. The application is a highly configurable Windows Service. The function is called with
Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);
, whereDataStore
is set to acomposite
which in turn callsParallel.ForEach(m_I
[AppDomain.FirstChanceException Event (System) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.firstchanceexception?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(System.AppDomain.FirstChanceException);k(TargetFrameworkMoniker-.NETCore,Version%3Dv5.0);k(DevLang-csharp)%26rd%3Dtrue&view=netframework-4.8)
The Master said, 'Am I indeed possessed of knowledge? I am not knowing. But if a mean person, who appears quite empty-like, ask anything of me, I set it forth from one end to the other, and exhaust it.' ― Confucian Analects
-
Weekend is approaching, and in case you may feel bored without a complicated programming task, I have a puzzle for you. I already asked at StackOverflow: c# - Logging exceptions in fire&forget tasks - Stack Overflow[^], but did not get a usable answer. In short, I want to create a method which takes an
Action
parameter, fires off aTask
with it, and makes sure that exceptions do not crash the application (but logs the exception messages so that I can find out that something bad happened). I tried following code:public static void CreateAndStartTaskWithErrorLogging(Action \_action, string \_componentName, string \_originalStacktrace = null) { DateTime started = HighPrecisionClock.Now; Task task = new Task(\_action); task.ContinueWith(\_continuation => \_continuation.LogExceptions(\_componentName, started, \_originalStacktrace)); task.ConfigureAwait(false); // tried also without this line task.Start(); } internal static void LogExceptions(this Task \_t, string \_componentName, DateTime \_started, string \_originalStacktrace = null) { try { \_t.Wait(1000); } catch (Exception ex) { Logger.LogError(\_componentName, $"An exception occurred in a fire-and-forget task which was started at {\_started}.\\r\\n" + $"The original stack trace is:\\r\\n{\_originalStacktrace}"); Logger.LogException(\_componentName, ex); } // tried also without this block try { \_t.Dispose(); } catch (Exception dex) { Logger.LogException(\_componentName, dex); } }
Well, normally, it works. Normally. Not always... And then the service crashes with an error entry in the Windows Event Log. (That's also true for the solution in the first answer at SO). Some more background. The application is a highly configurable Windows Service. The function is called with
Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);
, whereDataStore
is set to acomposite
which in turn callsParallel.ForEach(m_I
Bernhard Hiller wrote:
and makes sure that exceptions do not crash the application
Depending on your definition of terms that is impossible in C#. There are at least three exceptions which will cause a .Net AppDomain to exit and there is absolutely nothing you can do to stop it. One of those is the stack overflow exception. You can in fact 'catch' it but it will just keep going once the catch block exits. And it terminates the AppDomain not the process. "Starting with the .NET Framework 2.0, you can't catch a StackOverflowException object with a try/catch block, and the corresponding process is terminated by default." StackOverflowException Class (System) | Microsoft Docs[^] Now of course the workarounds for that depends on your definition. 1. Your main AppDomain (every process) could spin up a new AppDomain to do the actual launch. Then the 'application' can't exit only the new AppDomin will. Well at least not from this task. 2. Make sure the possible exceptions do not occur.
Bernhard Hiller wrote:
And then the service crashes wi
For starters looks like you are missing try/catches and logging. But other than that there can however be any number of reasons for that. For example consider the following scenario. 1. You have class A in DLL X. 2. You use A in your main service class in a method M() 3. You call M(). 4. Nothing else in X is used until M() is called. In the above X is not loaded until M() is called but it is loaded when M() is invoked. Now if X is not available you are going to get an exception. Something similar happens if you have a class C where attributes/implements uses C or where the methods have a return type or parameter that refers to C. So the best 'safe' way to create a service is as follows. Create a class, call it Everything, which does ALL of the functionality of the application. That means that the service class, the one with OnStart() does NOT do any application functionality. The service class looks like the following pseudo code
class MyService
{
// It MUST be 'Object' and not 'Everything'
// There should be NO other attributes an -
Native exceptions thrown by unmanaged code that occured on a thread started by that unmanaged code can't be caught. That's most often originating from a COM-object.
Bernhard Hiller wrote:
the exception may come from non-managed code.
Upvote is for you, as you answered it before me :)
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^] "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
Eddy Vluggen wrote:
Native exceptions thrown by unmanaged code that occured on a thread started by that unmanaged code can't be caught.
Thanks for that hint. Since most of these exceptions are caught by the catch, that seems unlikely (the stack trace, regardless if logged by my logger or the Windows Event Log, shows always the same function, i.e. these are not different kinds of exception, only different instances of the same exception). So, what are the circumstances that let 1 out of 10 of these exceptions evade? Is it something in the Parallel.ForEach which may or may not use a different thread for the execution?
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
[AppDomain.FirstChanceException Event (System) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.firstchanceexception?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(System.AppDomain.FirstChanceException);k(TargetFrameworkMoniker-.NETCore,Version%3Dv5.0);k(DevLang-csharp)%26rd%3Dtrue&view=netframework-4.8)
The Master said, 'Am I indeed possessed of knowledge? I am not knowing. But if a mean person, who appears quite empty-like, ask anything of me, I set it forth from one end to the other, and exhaust it.' ― Confucian Analects
"Occurs when an exception is thrown in managed code" - that won't work here, since the exception is thrown in unmanaged code.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
Bernhard Hiller wrote:
and makes sure that exceptions do not crash the application
Depending on your definition of terms that is impossible in C#. There are at least three exceptions which will cause a .Net AppDomain to exit and there is absolutely nothing you can do to stop it. One of those is the stack overflow exception. You can in fact 'catch' it but it will just keep going once the catch block exits. And it terminates the AppDomain not the process. "Starting with the .NET Framework 2.0, you can't catch a StackOverflowException object with a try/catch block, and the corresponding process is terminated by default." StackOverflowException Class (System) | Microsoft Docs[^] Now of course the workarounds for that depends on your definition. 1. Your main AppDomain (every process) could spin up a new AppDomain to do the actual launch. Then the 'application' can't exit only the new AppDomin will. Well at least not from this task. 2. Make sure the possible exceptions do not occur.
Bernhard Hiller wrote:
And then the service crashes wi
For starters looks like you are missing try/catches and logging. But other than that there can however be any number of reasons for that. For example consider the following scenario. 1. You have class A in DLL X. 2. You use A in your main service class in a method M() 3. You call M(). 4. Nothing else in X is used until M() is called. In the above X is not loaded until M() is called but it is loaded when M() is invoked. Now if X is not available you are going to get an exception. Something similar happens if you have a class C where attributes/implements uses C or where the methods have a return type or parameter that refers to C. So the best 'safe' way to create a service is as follows. Create a class, call it Everything, which does ALL of the functionality of the application. That means that the service class, the one with OnStart() does NOT do any application functionality. The service class looks like the following pseudo code
class MyService
{
// It MUST be 'Object' and not 'Everything'
// There should be NO other attributes anOf course, there are some exceptions which cannot be caught. In the current scenario, about 9 out of 10 instances of that Exception are caught, which offers proof that it is not an uncatchable exception.
jschell wrote:
For starters looks like you are missing try/catches and logging
You missed the point. The
try/catch
is exactly one of the things the utility function should supply (and so it does).Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
Eddy Vluggen wrote:
Native exceptions thrown by unmanaged code that occured on a thread started by that unmanaged code can't be caught.
Thanks for that hint. Since most of these exceptions are caught by the catch, that seems unlikely (the stack trace, regardless if logged by my logger or the Windows Event Log, shows always the same function, i.e. these are not different kinds of exception, only different instances of the same exception). So, what are the circumstances that let 1 out of 10 of these exceptions evade? Is it something in the Parallel.ForEach which may or may not use a different thread for the execution?
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
Bernhard Hiller wrote:
So, what are the circumstances that let 1 out of 10 of these exceptions evade? Is it something in the Parallel.ForEach which may or may not use a different thread for the execution?
No, more likely to be a thread by unmanaged code; avcodec_encode_video2 it was? Does it use DirectX?
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^] "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
-
Of course, there are some exceptions which cannot be caught. In the current scenario, about 9 out of 10 instances of that Exception are caught, which offers proof that it is not an uncatchable exception.
jschell wrote:
For starters looks like you are missing try/catches and logging
You missed the point. The
try/catch
is exactly one of the things the utility function should supply (and so it does).Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!