Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Catching exceptions in fire&forget tasks

Catching exceptions in fire&forget tasks

Scheduled Pinned Locked Moved C#
csharpcomdata-structuresdebugginghelp
10 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Bernhard Hiller
    wrote on last edited by
    #1

    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 a Task 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);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_I

    B L J 4 Replies Last reply
    0
    • B Bernhard Hiller

      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 a Task 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);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_I

      B Offline
      B Offline
      BillWoodruff
      wrote on last edited by
      #2

      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

      1 Reply Last reply
      0
      • B Bernhard Hiller

        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 a Task 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);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_I

        L Offline
        L Offline
        Lost User
        wrote on last edited by
        #3

        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.

        B 1 Reply Last reply
        0
        • B Bernhard Hiller

          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 a Task 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);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_I

          L Offline
          L Offline
          Lost User
          wrote on last edited by
          #4

          [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

          B 1 Reply Last reply
          0
          • B Bernhard Hiller

            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 a Task 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);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_I

            J Offline
            J Offline
            jschell
            wrote on last edited by
            #5

            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

            B 1 Reply Last reply
            0
            • L Lost User

              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.

              B Offline
              B Offline
              Bernhard Hiller
              wrote on last edited by
              #6

              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!

              L 1 Reply Last reply
              0
              • L Lost User

                [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

                B Offline
                B Offline
                Bernhard Hiller
                wrote on last edited by
                #7

                "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!

                1 Reply Last reply
                0
                • J jschell

                  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

                  B Offline
                  B Offline
                  Bernhard Hiller
                  wrote on last edited by
                  #8

                  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!

                  J 1 Reply Last reply
                  0
                  • B Bernhard Hiller

                    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!

                    L Offline
                    L Offline
                    Lost User
                    wrote on last edited by
                    #9

                    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.

                    1 Reply Last reply
                    0
                    • B Bernhard Hiller

                      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!

                      J Offline
                      J Offline
                      jschell
                      wrote on last edited by
                      #10

                      You should read my example again. There are catchable exceptions that your current code will not catch because they can occur before the try catch that you coded is entered.

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups