This class provides an easy way of retrying an action for a given number of seconds.
#region WatiN Copyright (C) 2006-2011 Jeroen van Menen
//Copyright 2006-2011 Jeroen van Menen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion Copyright
using System;
using System.Threading;
namespace WatiN.Core.UtilityClasses
{
public delegate T DoFunc<T>();
public delegate string BuildTimeOutExceptionMessage();
/// <summary>
/// This class provides an easy way of retrying an action for a given number of seconds.
/// <example>
/// The following code shows a basic usage:
/// <code>
/// var action = new TryFuncUntilTimeOut(5);
/// var result = action.Try(() => false == true);
/// </code>
/// </example>
/// </summary>
public class TryFuncUntilTimeOut
{
public readonly SimpleTimer _timer;
public readonly TimeSpan _timeout;
/// <summary>
/// Gets or sets the maximum interval between retries of the action.
/// </summary>
public TimeSpan SleepTime { get; set; }
/// <summary>
/// Returns the time out period.
/// </summary>
/// <value>The timeout.</value>
public TimeSpan Timeout
{
get { return _timer != null ? _timer.Timeout : _timeout; }
}
/// <summary>
/// Returns the last exception (thrown by the action) before the time out occured.
/// </summary>
/// <value>The last exception.</value>
public Exception LastException { get; set;}
/// <summary>
/// Returns a value indicating whether a time out occured.
/// </summary>
/// <value><c>true</c> if did time out; otherwise, <c>false</c>.</value>
public bool DidTimeOut { get; set;}
/// <summary>
/// Gets or sets the exception message. If set a <see cref="TimeoutException"/> will be thrown
/// if the action did time out.
/// </summary>
/// <value>The exception message.</value>
public BuildTimeOutExceptionMessage ExceptionMessage { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="TryFuncUntilTimeOut"/> class.
/// </summary>
/// <param name="timeout">The timeout in seconds.</param>
public TryFuncUntilTimeOut(TimeSpan timeout)
: this()
{
_timeout = timeout;
}
/// <summary>
/// Initializes a new instance of the <see cref="TryFuncUntilTimeOut"/> class.
/// </summary>
/// <param name="timer">The timer instance which will be used when executing <see cref="Try{T}(DoFunc{T})"/>.</param>
public TryFuncUntilTimeOut(SimpleTimer timer)
: this()
{
_timer = timer;
}
public TryFuncUntilTimeOut()
{
SleepTime = TimeSpan.FromMilliseconds(Settings.SleepTime);
}
public static T Try<T>(TimeSpan timeout, DoFunc<T> func)
{
var tryFunc = new TryFuncUntilTimeOut(timeout);
return tryFunc.Try(func);
}
/// <summary>
/// Tries the specified action until the result of the action is not equal to <c>default{T}</c>
/// or the time out is reached.
/// </summary>
/// <typeparam name="T">The result type of the action</typeparam>
/// <param name="func">The action.</param>
/// <returns>The result of the action of <c>default{T}</c> when time out occured.</returns>
public T Try<T>(DoFunc<T> func)
{
if (func == null) throw new ArgumentNullException("func");
var timeoutTimer = GetTimer();
var currentSleepTime = TimeSpan.FromMilliseconds(1);
do
{
LastException = null;
try
{
var result = func.Invoke();
if (!Equals(result, default(T)))
return result;
}
catch (Exception e)
{
LastException = e;
}
Sleep(currentSleepTime);
currentSleepTime += currentSleepTime;
if (currentSleepTime > SleepTime)
currentSleepTime = SleepTime;
} while (!timeoutTimer.Elapsed);
HandleTimeOut();
return default(T);
}
public virtual void Sleep(TimeSpan sleepTime)
{
Thread.Sleep(sleepTime);
}
public SimpleTimer GetTimer()
{
return _timer ?? new SimpleTimer(Timeout);
}
public void HandleTimeOut()
{
DidTimeOut = true;
if (ExceptionMessage != null)
{
ThrowTimeOutException(LastException, ExceptionMessage.Invoke());
}
}
public static void ThrowTimeOutException(Exception lastException, string message)
{
if (lastException != null)
{
throw new Exceptions.TimeoutException(message, lastException);
}
throw new Exceptions.TimeoutException(message);
}
}
}