MSMVPS.COM
The Ultimate Destination for Blogs by Current and Former Microsoft Most Valuable Professionals.

Implementing Timeout & Retry

I have come across several situations where I need some sort of Timeout functionality for a particular operation. At the same time, I also needed a provision for retrying the operation a few times before calling quits. I thought about a solution using ManualResetEvent and a Timer which I have outlined below:

const int MAX_RETRIES = 5;
const int RETRY_INTERVAL = 10;    //10 seconds
const int OPERATION_TIMEOUT = 1; //1 minute

static void DoOperation()
{
    
int retries = 0;
    
int timeout = 0;

    ManualResetEvent waitHandle = 
new ManualResetEvent(false);

    
//Use a timer to retry the opeation periodically
    
Timer retryTimer = null;
    retryTimer = 
new Timer(
           
new TimerCallback(
               
//The operation to carry out is represented by this anonymous method.
               
delegate(object state)
               {
                   
try
                   
{
                       
if (retries > 0)
                       {
                           
//The operation has failed before. So you may want to handle 
                           // that situation here.
                           
Console.WriteLine("Retry attempt: {0}", retries);
                       }

                       
//Do the operation which may fail or timeout. 
                       
Console.WriteLine("Doing some major operation now.");

                       
//throw new Exception("Some exception");
                       //Thread.Sleep(1000);

                       //Checking whether the operation timed out. In this case, the
                       // thread isnt forcefully aborted, so you can exit gracefully
                       //Else, you may need to handle the ThreadAbortException
                       
if (timeout == ManualResetEvent.WaitTimeout)
                       {
                           Console.WriteLine("It seems that the operation timed out. Need to exit gracefully");
                       }
                       
else
                       
{
                           Console.WriteLine("Operation completed");
                       }

                       
//Set the waithandle if the opeation is successful
                       
waitHandle.Set();

                       
//Everything went well. Close the timer.
                       
retryTimer.Dispose();
                   }
                   
catch (Exception e)
                   {
                       
// Handle the exception

                       
retries++;

                       
//Check if the number of retries has exceeded the maximum allowed
                       
if (retries > MAX_RETRIES)
                       {
                           
//Do some logic to handle this situation
                           
Console.WriteLine("Operation has exceeded maximum attempts");

                           
// Set the waitHandle so that the method returns
                           
waitHandle.Set();
                       }
                       
else
                       
{
                           
//Retry the operation or any do alternative logic here
                           
Console.WriteLine("The operation failed and will be retried in a short while.");
                       }
                   }
               }),
           
null,
           
new TimeSpan(0, 0, 0),
           
new TimeSpan(0, 0, RETRY_INTERVAL)); // The timer retries the operation for the specified interval

    //Wait for the operation to complete or timeout.
    
timeout = ManualResetEvent.WaitAny(
       
new WaitHandle[] { waitHandle },
       (
int)new TimeSpan(0, OPERATION_TIMEOUT, 0).TotalMilliseconds,
       
false);

    
if (timeout == ManualResetEvent.WaitTimeout)
    {
        
// The operation timed out. Abort the operation thread or exit gracefully.
        
Console.WriteLine("The operation timed out.");
    }

    waitHandle.Close();
    retryTimer.Dispose();
}

Let me explain this code section which may look a little convoluted at first. One of the easiest ways of implementing the timeout functionality is using WaitHandles. In the sample above, I have used a ManualResetEvent, but I guess you may use other WaitHandles like AutoResetEvent** etc, as you see fit. The idea is to spawn off a thread to execute an operation (which could possibly be a long one), and then wait for the thread to signal completion through the ManualResetEvent’s Set method. The ManualResetEvent provides a static method called WaitAny which can be used to wait for a specified period of time on one or many waithandles (In our case, it is just one). 

Retry logic can be implemented using a Timer (System.Threading). The Timer event is where the meat of the work is done. The exception handler basically does the work of checking if further retries are required by checking a counter. Once, the process successfully completes, the WaitHandle is Set and the main thread is signaled.  

**At first, it may be difficult to differentiate  or choose between ManualResetEvent and AutoResetEvent. AutoResetEvent is analogous to the doorbell switch, which goes back to the off state once you press and release it. AutoResetEvent resets itself automatically once you set it. ManualResetEvent, on the other hand, does not reset automatically - it is like a normal switch. Therefore, ManualResetEvent can be used when signaling happens once and for all, and subsequently, threads need not wait again for another signal. AutoResetEvent , on the other hand, can be used for finer coordination between threads, when signaling can happen intermittently.

*Disclaimer:
I don’t suppose this is the best way to achieve timeout and retry – this is just one way. Do let me know if there are better techniques!


Posted Nov 01 2006, 05:58 PM by Manoj G
Filed under:

Add a Comment

(required)  
(optional)
(required)  
Remember Me?


Copyright © is the original authors. Blog site is an independent site not sponsored by Microsoft. The Yoda blog server and the Brianna SQL server would like to thank www.ownwebnow.com and www.exchangedefender.com. They wouldn't be here and broadcasting without the generosity of Vlad Mazek and his companies.

Powered by Community Server (Commercial Edition), by Telligent Systems