Created
June 18, 2012 19:29
-
-
Save pckujawa/2950232 to your computer and use it in GitHub Desktop.
Helper class for efficiently invoking tasks with timeout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region (c)2009-2011 Lokad - New BSD license | |
// Copyright (c) Lokad 2009 | |
// Company: http://www.lokad.com | |
// This code is released under the terms of the new BSD licence | |
#endregion | |
#if !SILVERLIGHT2 | |
using System; | |
using System.Threading; | |
using Lokad.Quality; | |
namespace Lokad.Threading | |
{ | |
/// <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 specified 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); | |
} | |
} | |
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 specified 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); | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Copied from https://github.com/Lokad/lokad-shared-libraries/blob/master/Source/Lokad.Shared/Threading/WaitFor.cs