Allowing timeout on long-running operations - possible bug

Tags: .NET, C#, CodeSnippets

A while ago, I wrote about a simple pattern to allow us to put a timeout limitation on a long running operations. Simply put, it allows us to replace this:

public MyObject GetData()
{
   try
   {
      MyObject data = BigComponent.LongRunningOperation();
      return data;
   }
   catch (Exception ex)
   {
      // Log and rethrow.
      throw;
   }
}

with this:

public MyObject GetData()
{
   try
   {
      MyObject data;
      Thread t = new Thread(
                           delegate()
                           {
                              data = BigComponent.LongRunningOperation();
                           });
      t.Start();
      bool success = t.Join(timeoutForOperation);
      if (success)
      {
         reutrn data;
      }
      else
      {
         throw new TimeoutException();
      }
  
      return data;
   }
   catch (Exception ex)
   {
      // Log and rethrow.
      throw;
   }
}

This pattern, while simple at first, introduced a bug into my design that in retrospect should be glaringly obvious. The bug is that although my component previously caught exceptions in LongRunningOperation, this method is now called in a different thread and not handled by my try/catch. This is hard to see when using anonymous delegates, since you get the feeling that it's still a part of the parent method.

In .NET 2.0, the default behavior for an unhandled exception in a thread is to abort the whole process. In my case, it was an out-of-process COM+ service doing some heavy data crunching for us, and the result was a pop-up(!) on the server complaining that the COM Surrogate crashed. Took me a while to figure out what I did wrong.

One way of solving this is to simply wrap the code i nside the delegate with try/catch and swallow the exception. But that way I lose the information about the inner exception, which I want to propagate upwards. What I ended up doing is somewhat uglier, and involves passing the exception backwards the way I did with the data:

MyObject data;
Exception exceptionInProcess;
Thread t = new Thread(
                     delegate()
                     {
                        try
                        {
                           data = BigComponent.LongRunningOperation();
                        }
                        catch (Exception ex)
                        {
                           exceptionInProcess = ex;
                        });
...
if (exceptionInProcess != null)
{
   throw exceptionInProcess;
}

2 Comments

Comments have been disabled for this content.