Implement C# Generic Timeout

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

}

How to create a generic timeout object for various code blocks?

OK, I've spent some time on this one and I think I have a solution that will work for you without having to change your code all that much.

The following is how you would use the Timebox class that I created.

public void MyMethod( ... ) {

// some stuff

// instead of this
// using(...){ /* your code here */ }

// you can use this
var timebox = new Timebox(TimeSpan.FromSeconds(1));
timebox.Execute(() =>
{
/* your code here */
});

// some more stuff

}

Here's how Timebox works.

  • A Timebox object is created with a given Timespan
  • When Execute is called, the Timebox creates a child AppDomain to hold a TimeboxRuntime object reference, and returns a proxy to it
  • The TimeboxRuntime object in the child AppDomain takes an Action as input to execute within the child domain
  • Timebox then creates a task to call the TimeboxRuntime proxy
  • The task is started (and the action execution starts), and the "main" thread waits for for as long as the given TimeSpan
  • After the given TimeSpan (or when the task completes), the child AppDomain is unloaded whether the Action was completed or not.
  • A TimeoutException is thrown if action times out, otherwise if action throws an exception, it is caught by the child AppDomain and returned for the calling AppDomain to throw

A downside is that your program will need elevated enough permissions to create an AppDomain.

Here is a sample program which demonstrates how it works (I believe you can copy-paste this, if you include the correct usings). I also created this gist if you are interested.

public class Program
{
public static void Main()
{
try
{
var timebox = new Timebox(TimeSpan.FromSeconds(1));
timebox.Execute(() =>
{
// do your thing
for (var i = 0; i < 1000; i++)
{
Console.WriteLine(i);
}
});

Console.WriteLine("Didn't Time Out");
}
catch (TimeoutException e)
{
Console.WriteLine("Timed Out");
// handle it
}
catch(Exception e)
{
Console.WriteLine("Another exception was thrown in your timeboxed function");
// handle it
}
Console.WriteLine("Program Finished");
Console.ReadLine();
}
}

public class Timebox
{
private readonly TimeSpan _ts;

public Timebox(TimeSpan ts)
{
_ts = ts;
}

public void Execute(Action func)
{
AppDomain childDomain = null;
try
{
// Construct and initialize settings for a second AppDomain. Perhaps some of
// this is unnecessary but perhaps not.
var domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
};

// Create the child AppDomain
childDomain = AppDomain.CreateDomain("Timebox Domain", null, domainSetup);

// Create an instance of the timebox runtime child AppDomain
var timeboxRuntime = (ITimeboxRuntime)childDomain.CreateInstanceAndUnwrap(
typeof(TimeboxRuntime).Assembly.FullName, typeof(TimeboxRuntime).FullName);

// Start the runtime, by passing it the function we're timboxing
Exception ex = null;
var timeoutOccurred = true;
var task = new Task(() =>
{
ex = timeboxRuntime.Run(func);
timeoutOccurred = false;
});

// start task, and wait for the alloted timespan. If the method doesn't finish
// by then, then we kill the childDomain and throw a TimeoutException
task.Start();
task.Wait(_ts);

// if the timeout occurred then we throw the exception for the caller to handle.
if(timeoutOccurred)
{
throw new TimeoutException("The child domain timed out");
}

// If no timeout occurred, then throw whatever exception was thrown
// by our child AppDomain, so that calling code "sees" the exception
// thrown by the code that it passes in.
if(ex != null)
{
throw ex;
}
}
finally
{
// kill the child domain whether or not the function has completed
if(childDomain != null) AppDomain.Unload(childDomain);
}
}

// don't strictly need this, but I prefer having an interface point to the proxy
private interface ITimeboxRuntime
{
Exception Run(Action action);
}

// Need to derive from MarshalByRefObject... proxy is returned across AppDomain boundary.
private class TimeboxRuntime : MarshalByRefObject, ITimeboxRuntime
{
public Exception Run(Action action)
{
try
{
// Nike: just do it!
action();
}
catch(Exception e)
{
// return the exception to be thrown in the calling AppDomain
return e;
}
return null;
}
}
}

EDIT:

The reason I went with an AppDomain instead of Threads or Tasks only, is because there is no bullet proof way for terminating Threads or Tasks for arbitrary code [1][2][3]. An AppDomain, for your requirements, seemed like the best approach to me.

Implementing timeout for calling generic function with parameters

At first it should probably be

static void CallAndWait<T>(Action<T, T> action, int timeout)

instead of

static void CallAndWait(Action<T, T> action, int timeout)

and if the parameters have different types even the following.

static void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)

But I don't think that is all. Going to look at it again.

UPDATE

Now I can see your problem ... you are calling the action when you try to call CallAndWait(). The call must be the following

CallWithTimeout.CallAndWait(action, 1500, "hello", 500);

instead of your call.

CallWithTimeout.CallAndWait<int, string>(action(1500, "hello"), 500);

So you have to change the method signature from

void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)

to

void CallAndWait<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2, int timeout)

modify the body a bit and you should be done.

Implement c# timeout

As I understand it, you want your method to do some work until it's done or until some period of time has elapsed? I would use a Stopwatch for that, and check the elapsed time in a loop:

void DoWork()
{
// we'll stop after 10 minutes
TimeSpan maxDuration = TimeSpan.FromMinutes(10);
Stopwatch sw = Stopwatch.StartNew();
DoneWithWork = false;

while (sw.Elapsed < maxDuration && !DoneWithWork)
{
// do some work
// if all the work is completed, set DoneWithWork to True
}

// Either we finished the work or we ran out of time.
}

Generic timeout function without thread.abort?

Well I guess I don't need to use generics, I found the cause of the root problem with amazon AWS. Apparently, if the name of the bucket has a period in it and the region isnt US(east), a specific endpoint needs to be configured for the client.

Implementing a timeout on a function returning a value

Here is a generic solution that allows you to wrap any method in a timeout:

http://kossovsky.net/index.php/2009/07/csharp-how-to-limit-method-execution-time/

It uses the useful Thread.Join overload that accepts a timeout in milliseconds rather than manually using timers. The only thing I would do differently is swap the success flag and result value to match the TryParse pattern, as follows:

public static T Execute<T>(Func<T> func, int timeout)
{
T result;
TryExecute(func, timeout, out result);
return result;
}

public static bool TryExecute<T>(Func<T> func, int timeout, out T result)
{
var t = default(T);
var thread = new Thread(() => t = func());
thread.Start();
var completed = thread.Join(timeout);
if (!completed) thread.Abort();
result = t;
return completed;
}

And this is how you would use it:

var func = new Func<string>(() =>
{
Thread.Sleep(200);
return "success";
});
string result;
Debug.Assert(!TryExecute(func, 100, out result));
Debug.Assert(result == null);
Debug.Assert(TryExecute(func, 300, out result));
Debug.Assert(result == "success");

You could also add overloads that accept Action instead of Func if you want to execute a method that doesn't return a value.

Implement Timeout for function C#

You can use Tasks to do so:

Task<HideReport> myTask = Task.Factory.StartNew(() => _choosendevice.ReadReport(););
myTask.Wait(100); //Wait for 100 ms.

if (myTask.IsCompleted)
Console.WriteLine("myTask completed.");
else
Console.WriteLine("Timed out before myTask completed.");

HidReport report = myTask.Result;

EDIT I didn't know the return value of your function. It returns a HidReport objtect. I just modified the Task creation to fit the return type

As said in comments the library already provides this mechanism so you just can call the right method

HidReport report = await ReadReportAsync(timeout);

** EDIT ** This code gone well for me

HidDevice device = HidDevices.Enumerate().ToList().First(e =>e.Description.Contains("mouse"));

Task<HidReport> t = Task.Factory.StartNew(() => device.ReadReport(1000));

t.Wait();

HidReport report = t.Result;

timing out a method call

You could use Tasks to do this, here's an example:

public string AskAPI(string uri, int millis)
{
using (var task = new Task<string>(() => api.Query(uri)))
{
task.Start();
task.Wait(millis);
if (!task.IsCompleted) throw new TimeoutException();
return task.Result;
}
}

Thanks to Guillaume for suggesting to use a TimeoutException.

But as Jim Mischel points out, this won't stop the task from completing, in which case it'd be best if you could modify the API you're calling because then you could make full use of the CancellationToken class which was made for this kind of thing.

Other than that, the only other quick solution I can think of (which may be inadvisable) would be to wrap the method (or combine it) with something like this:

public T Await<T>(Func<T> fun, int millis)
{
using (var cancel = new CancellationTokenSource(millis))
using (var task = new Task<T>(() =>
{
T result = default(T);
var thread = new Thread(() => result = fun());
thread.Start();
while (!cancel.Token.IsCancellationRequested && thread.IsAlive) ; // Wait for a sign from above
thread.Abort();
cancel.Token.ThrowIfCancellationRequested();
return result;
}, cancel.Token))
{
task.Start();
task.Wait(millis);
cancel.Cancel();
if (!task.IsCompleted) throw new TimeoutException();
return task.Result;
}
}

And then call it using:

Await(() => AskAPI("http://some.uri"), 30000);

Set timeout to an operation

You could run the operation in a separate thread and then put a timeout on the thread join operation:

using System.Threading;

class Program {
static void DoSomething() {
try {
// your call here...
obj.PerformInitTransaction();
} catch (ThreadAbortException) {
// cleanup code, if needed...
}
}

public static void Main(params string[] args) {

Thread t = new Thread(DoSomething);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(30))) {
t.Abort();
throw new Exception("More than 30 secs.");
}
}
}


Related Topics



Leave a reply



Submit