I need a time-out! Or more specifically, my methods need a time-out mechanism. And because I spent seven projects and quite some time on the web trying to find out how to do it correctly, I am writing this small article about the works.
A method with time-out capabilities will perform the action the user wants until a specific time has elapsed, then an error is returned. There are a few ways to implement it, the first method that I tried is the following:
- Start a timer that will callback 1 time after x milliseconds
- Start the work that needs to be done
- Check if the timer has called it's callback method
- Throw exception if timer did
- Return normally
This idea is plane wrong. The method will first wait for the work to complete and next verify the elapsed time. This way we have gained no time at all. We need to rework this schema:
MainMethod:
- Start a timer that will callback 1 time after x milliseconds
- Start the work
- Return normally
TimerCallback:
- Throw exception
Ok, let's try this one. The idea is getting better, but there is still a problem. The callback method of the timer is handled by a thread in the ThreadPool . When an exception is thrown inside that thread, there is no way to handle it correctly in the main thread where the method with time-out is functioning. When the exception is thrown an uncaught exception occurs, not very user friendly. Again a rework:
MainMethod:
- Start a delegate to do the requested work
- Wait for x milliseconds for the delegate to finish
- Throw exception when delegate not finished
- Return normally
Delegate method:
- Start the work that needs to be done
Now we're getting somewhere, the exception is thrown in the main method and the work is done separately. The last thing that's needed is a translation into code.
delegate object WorkDelegate(string arg);
public object DoWork(string arg)
{
WorkDelegate d = new WorkDelegate(DoWorkHandler);
IAsyncResult res = d.BeginInvoke(arg,null,null);
if(res.IsCompleted == false)
{
res.AsyncWaitHandle.WaitOne(10000,false);
if(res.IsCompleted == false)
throw new ApplicationException("Timeout");
}
return d.EndInvoke((AsyncResult)res);
}
private object DoWorkHandler(string arg)
{
Thread.Sleep(5000);
return "blah " + arg;
}
Here's the breakdown. The public method is the one called by the user of the class. It will first create a delegate to do the actual work. The undocumented BeginInvoke
method is called to start the work asynchronously. Next a check is made to see if the delegate has finished already, if not we wait and check again. Using the now obtained info it is possible to either throw an exception, or to return normally using the, again undocumented, EndInvoke
method. Quite simple, isn't it?
To try it all out I included a test application, fiddle around with the wait time in the delegate method and the main method to see it all working.
Comments