Implement C# Generic Timeout Implement C# Generic Timeout multithreading multithreading

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.

enter image description here


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