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;
}