Implement C# Generic Timeout
The really tricky part here was killing the long running task through passing the executor thread from the Action back to a place where it could be aborted. I accomplished this with the use of a wrapped delegate that passes out the thread to kill into a local variable in the method that created the lambda.
I submit this example, for your enjoyment. The method you are really interested in is CallWithTimeout. This will cancel the long running thread by aborting it, and swallowing the ThreadAbortException:
Usage:
class Program{ static void Main(string[] args) { //try the five second method with a 6 second timeout CallWithTimeout(FiveSecondMethod, 6000); //try the five second method with a 4 second timeout //this will throw a timeout exception CallWithTimeout(FiveSecondMethod, 4000); } static void FiveSecondMethod() { Thread.Sleep(5000); }
The static method doing the work:
static void CallWithTimeout(Action action, int timeoutMilliseconds) { Thread threadToKill = null; Action wrappedAction = () => { threadToKill = Thread.CurrentThread; try { action(); } catch(ThreadAbortException ex){ Thread.ResetAbort();// cancel hard aborting, lets to finish it nicely. } }; IAsyncResult result = wrappedAction.BeginInvoke(null, null); if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) { wrappedAction.EndInvoke(result); } else { threadToKill.Abort(); throw new TimeoutException(); } }}
We are using code like this heavily in production:
var result = WaitFor<Result>.Run(1.Minutes(), () => service.GetSomeFragileResult());
Implementation is open-sourced, works efficiently even in parallel computing scenarios and is available as a part of Lokad Shared Libraries
/// <summary>/// Helper class for invoking tasks with timeout. Overhead is 0,005 ms./// </summary>/// <typeparam name="TResult">The type of the result.</typeparam>[Immutable]public sealed class WaitFor<TResult>{ readonly TimeSpan _timeout; /// <summary> /// Initializes a new instance of the <see cref="WaitFor{T}"/> class, /// using the specified timeout for all operations. /// </summary> /// <param name="timeout">The timeout.</param> public WaitFor(TimeSpan timeout) { _timeout = timeout; } /// <summary> /// Executes the spcified function within the current thread, aborting it /// if it does not complete within the specified timeout interval. /// </summary> /// <param name="function">The function.</param> /// <returns>result of the function</returns> /// <remarks> /// The performance trick is that we do not interrupt the current /// running thread. Instead, we just create a watcher that will sleep /// until the originating thread terminates or until the timeout is /// elapsed. /// </remarks> /// <exception cref="ArgumentNullException">if function is null</exception> /// <exception cref="TimeoutException">if the function does not finish in time </exception> public TResult Run(Func<TResult> function) { if (function == null) throw new ArgumentNullException("function"); var sync = new object(); var isCompleted = false; WaitCallback watcher = obj => { var watchedThread = obj as Thread; lock (sync) { if (!isCompleted) { Monitor.Wait(sync, _timeout); } } // CAUTION: the call to Abort() can be blocking in rare situations // http://msdn.microsoft.com/en-us/library/ty8d3wta.aspx // Hence, it should not be called with the 'lock' as it could deadlock // with the 'finally' block below. if (!isCompleted) { watchedThread.Abort(); } }; try { ThreadPool.QueueUserWorkItem(watcher, Thread.CurrentThread); return function(); } catch (ThreadAbortException) { // This is our own exception. Thread.ResetAbort(); throw new TimeoutException(string.Format("The operation has timed out after {0}.", _timeout)); } finally { lock (sync) { isCompleted = true; Monitor.Pulse(sync); } } } /// <summary> /// Executes the spcified function within the current thread, aborting it /// if it does not complete within the specified timeout interval. /// </summary> /// <param name="timeout">The timeout.</param> /// <param name="function">The function.</param> /// <returns>result of the function</returns> /// <remarks> /// The performance trick is that we do not interrupt the current /// running thread. Instead, we just create a watcher that will sleep /// until the originating thread terminates or until the timeout is /// elapsed. /// </remarks> /// <exception cref="ArgumentNullException">if function is null</exception> /// <exception cref="TimeoutException">if the function does not finish in time </exception> public static TResult Run(TimeSpan timeout, Func<TResult> function) { return new WaitFor<TResult>(timeout).Run(function); }}
This code is still buggy, you can try with this small test program:
static void Main(string[] args) { // Use a sb instead of Console.WriteLine() that is modifying how synchronous object are working var sb = new StringBuilder(); for (var j = 1; j < 10; j++) // do the experiment 10 times to have chances to see the ThreadAbortException for (var ii = 8; ii < 15; ii++) { int i = ii; try { Debug.WriteLine(i); try { WaitFor<int>.Run(TimeSpan.FromMilliseconds(10), () => { Thread.Sleep(i); sb.Append("Processed " + i + "\r\n"); return i; }); } catch (TimeoutException) { sb.Append("Time out for " + i + "\r\n"); } Thread.Sleep(10); // Here to wait until we get the abort procedure } catch (ThreadAbortException) { Thread.ResetAbort(); sb.Append(" *** ThreadAbortException on " + i + " *** \r\n"); } } Console.WriteLine(sb.ToString()); } }
There is a race condition. It is clearly possible that a ThreadAbortException gets raised after the method WaitFor<int>.Run()
is being called. I didn't find a reliable way to fix this, however with the same test I cannot repro any problem with the TheSoftwareJedi accepted answer.
Well, you could do things with delegates (BeginInvoke, with a callback setting a flag - and the original code waiting for that flag or timeout) - but the problem is that it is very hard to shut down the running code. For example, killing (or pausing) a thread is dangerous... so I don't think there is an easy way to do this robustly.
I'll post this, but note it is not ideal - it doesn't stop the long-running task, and it doesn't clean up properly on failure.
static void Main() { DoWork(OK, 5000); DoWork(Nasty, 5000); } static void OK() { Thread.Sleep(1000); } static void Nasty() { Thread.Sleep(10000); } static void DoWork(Action action, int timeout) { ManualResetEvent evt = new ManualResetEvent(false); AsyncCallback cb = delegate {evt.Set();}; IAsyncResult result = action.BeginInvoke(cb, null); if (evt.WaitOne(timeout)) { action.EndInvoke(result); } else { throw new TimeoutException(); } } static T DoWork<T>(Func<T> func, int timeout) { ManualResetEvent evt = new ManualResetEvent(false); AsyncCallback cb = delegate { evt.Set(); }; IAsyncResult result = func.BeginInvoke(cb, null); if (evt.WaitOne(timeout)) { return func.EndInvoke(result); } else { throw new TimeoutException(); } }