Implement C# Generic Timeout

C#MultithreadingC# 3.0AsynchronousTimeout

C# Problem Overview


I am looking for good ideas for implementing a generic way to have a single line (or anonymous delegate) of code execute with a timeout.

TemperamentalClass tc = new TemperamentalClass();
tc.DoSomething();  // normally runs in 30 sec.  Want to error at 1 min

I'm looking for a solution that can elegantly be implemented in many places where my code interacts with temperamental code (that I can't change).

In addition, I would like to have the offending "timed out" code stopped from executing further if possible.

C# Solutions


Solution 1 - C#

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

}

Solution 2 - C#

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

Solution 3 - C#

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

Solution 4 - C#

Some minor changes to Pop Catalin's great answer:

  • Func instead of Action
  • Throw exception on bad timeout value
  • Calling EndInvoke in case of timeout

Overloads have been added to support signaling worker to cancel execution:

public static T Invoke<T> (Func<CancelEventArgs, T> function, TimeSpan timeout) {
    if (timeout.TotalMilliseconds <= 0)
        throw new ArgumentOutOfRangeException ("timeout");

    CancelEventArgs args = new CancelEventArgs (false);
    IAsyncResult functionResult = function.BeginInvoke (args, null, null);
    WaitHandle waitHandle = functionResult.AsyncWaitHandle;
    if (!waitHandle.WaitOne (timeout)) {
        args.Cancel = true; // flag to worker that it should cancel!
        /* •————————————————————————————————————————————————————————————————————————•
           | IMPORTANT: Always call EndInvoke to complete your asynchronous call.   |
           | http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.80).aspx           |
           | (even though we arn't interested in the result)                        |
           •————————————————————————————————————————————————————————————————————————• */
        ThreadPool.UnsafeRegisterWaitForSingleObject (waitHandle,
            (state, timedOut) => function.EndInvoke (functionResult),
            null, -1, true);
        throw new TimeoutException ();
    }
    else
        return function.EndInvoke (functionResult);
}

public static T Invoke<T> (Func<T> function, TimeSpan timeout) {
    return Invoke (args => function (), timeout); // ignore CancelEventArgs
}

public static void Invoke (Action<CancelEventArgs> action, TimeSpan timeout) {
    Invoke<int> (args => { // pass a function that returns 0 & ignore result
        action (args);
        return 0;
    }, timeout);
}

public static void TryInvoke (Action action, TimeSpan timeout) {
    Invoke (args => action (), timeout); // ignore CancelEventArgs
}

Solution 5 - C#

This is how I'd do it:

public static class Runner
{
    public static void Run(Action action, TimeSpan timeout)
    {
        IAsyncResult ar = action.BeginInvoke(null, null);
        if (ar.AsyncWaitHandle.WaitOne(timeout))
            action.EndInvoke(ar); // This is necesary so that any exceptions thrown by action delegate is rethrown on completion
        else
            throw new TimeoutException("Action failed to complete using the given timeout!");
    }
}

Solution 6 - C#

I just knocked this out now so it might need some improvement, but will do what you want. It is a simple console app, but demonstrates the principles needed.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;


namespace TemporalThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            Action action = () => Thread.Sleep(10000);
            DoSomething(action, 5000);
            Console.ReadKey();
        }

        static void DoSomething(Action action, int timeout)
        {
            EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
            AsyncCallback callback = ar => waitHandle.Set();
            action.BeginInvoke(callback, null);

            if (!waitHandle.WaitOne(timeout))
                throw new Exception("Failed to complete in the timeout specified.");
        }
    }

}

Solution 7 - C#

What about using Thread.Join(int timeout)?

public static void CallWithTimeout(Action act, int millisecondsTimeout)
{
	var thread = new Thread(new ThreadStart(act));
	thread.Start();
	if (!thread.Join(millisecondsTimeout))
		throw new Exception("Timed out");
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionchilltempView Question on Stackoverflow
Solution 1 - C#TheSoftwareJediView Answer on Stackoverflow
Solution 2 - C#Rinat AbdullinView Answer on Stackoverflow
Solution 3 - C#Marc GravellView Answer on Stackoverflow
Solution 4 - C#George TsiokosView Answer on Stackoverflow
Solution 5 - C#Pop CatalinView Answer on Stackoverflow
Solution 6 - C#Jason JacksonView Answer on Stackoverflow
Solution 7 - C#eacousineauView Answer on Stackoverflow