Platform: Silverlight 4, .NET 4
With the .NET 4.5 Developer preview the RegEx class has been enhanced to allow setting of a Timeout value which would prevent the Reg
I re-implemented the above code changing it in a way that I belive is more reliable.
/// <summary>
/// Executes function proc on a separate thread respecting the given timeout value.
/// </summary>
/// <typeparam name="R"></typeparam>
/// <param name="proc">The function to execute.</param>
/// <param name="timeout">The timeout duration.</param>
/// <returns></returns>
public static R ExecuteWithTimeout<R>(Func<R> proc, TimeSpan timeout) {
var r = default(R); // init default return value
Exception ex = null; // records inter-thread exception
// define a thread to wrap 'proc'
var t = new Thread(() => {
try {
r = proc();
}
catch (Exception e) {
// this can get set to ThreadAbortException
ex = e;
Debug.WriteLine("Exception hit");
}
});
t.Start(); // start running 'proc' thread wrapper
// from docs: "The Start method does not return until the new thread has started running."
if (t.Join(timeout) == false) {
t.Abort(); // die evil thread!
// Abort raises the ThreadAbortException
int i = 0;
while ((t.Join(1) == false) && (i < 20)) { // 20 ms wait possible here
i++;
}
if (i >= 20) {
// we didn't abort, might want to log this or take some other action
// this can happen if you are doing something indefinitely hinky in a
// finally block (cause the finally be will executed before the Abort
// completes.
Debug.WriteLine("Abort didn't work as expected");
}
}
if (ex != null) {
throw ex; // oops
}
return r; // ah!
}
The standard way to get a timeout on something that doesn't already come with the feature, is to simply start whatever you want to process on a separate thread, and then in your main thread, you use Thread.Join with the appropriate timeout.
It is not that simple - but it can be done using two threads with the first doing the regex, the second killing the first thread if itruns too long. This is problematic in itself, though.
Generic example:
public static R WithTimeout<R>(Func<R> proc, int duration)
{
var wh = proc.BeginInvoke(null, null);
if (wh.AsyncWaitHandle.WaitOne(duration))
{
return proc.EndInvoke(wh);
}
throw new TimeOutException();
}
Usage:
var r = WithTimeout(() => regex.Match(foo), 1000);
Update:
As pointed out by Christian.K, the async thread will still continue running.
Here is one where the thread will terminate:
public static R WithTimeout<R>(Func<R> proc, int duration)
{
var reset = new AutoResetEvent(false);
var r = default(R);
Exception ex = null;
var t = new Thread(() =>
{
try
{
r = proc();
}
catch (Exception e)
{
ex = e;
}
reset.Set();
});
t.Start();
// not sure if this is really needed in general
while (t.ThreadState != ThreadState.Running)
{
Thread.Sleep(0);
}
if (!reset.WaitOne(duration))
{
t.Abort();
throw new TimeoutException();
}
if (ex != null)
{
throw ex;
}
return r;
}
Update:
Fixed above snippet to deal with exceptions correctly.