using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DTS.Common.Utilities
{
///
/// this is a class to simplify waiting for a resource
/// it can wait for a specified period and also check another signal or condition
///
public class WaitWithCondition
{
///
/// the max time to wait in any spin cycle
///
/// this should probably be private and never modified as it will affect all
/// waitwithcondition instances.
/// if a wait condition is created with a 1000 millisecond wait period, and the EachWaitMilliSec
/// was 20, we would wait for 50 cycles of 20ms before our final timeout
///
public static int EachWaitMilliSec = 20; // we wait 0.02 second each time
///
/// optional function to check while waiting for handle to return.
///
/// bool if condition has been met, false otherwise
public delegate bool Condition();
///
/// this exception is used to indicate a condition was met to the calling code
///
public class ConditionMetException : ApplicationException
{
}
///
/// waits either for work to be finished (signaled by workevent),
/// timeout, or the work to be canceled
/// returns false if timed out, returns true if work completed
/// throws an exception if canceled?
/// (note this should be the same behavior as Wait(handle, int, condition)
/// 17600 Communication class improvements from 3.2
///
public static bool Wait(WaitHandle workEvent, int toMS, WaitHandle cancelEvent)
{
var res = WaitHandle.WaitAny(new[] { workEvent, cancelEvent }, toMS);
//nothing signalled
if (WaitHandle.WaitTimeout == res) { return false; }
if (0 == res) { return true; }
throw new ConditionMetException();
}
///
/// wait for a resource to be available
///
/// object to wait on
/// max amount of time in milliseconds to wait
/// use to wait for the max amount of time
/// allowed.
///
/// an additional condition to check. Can be null.
/// if condition is met before resource is available a
/// is thrown.
///
/// true indicates the object has returned within
/// the requested time period, false otherwise
///
public static bool Wait(WaitHandle handle, int toMilliSec, Condition condition)
{
var NumberOfWaits = 1;
// check for infinity
if (toMilliSec < 0)
{
if (toMilliSec != System.Threading.Timeout.Infinite)
{
throw new ArgumentException("WaitWithCondition.Wait: invalid timeout value");
}
// they want to wait forever, a billion seconds is pretty long
NumberOfWaits = 1000000000;
}
else if (toMilliSec == 0)
{
// 0 means no wait just check
EachWaitMilliSec = 0;
}
else
{
// they want to wait X milli seconds,
// first make sure that isn't shorter than our eachWaitMilliSec
if (toMilliSec < EachWaitMilliSec)
{
// yes it is, adjust our wait
EachWaitMilliSec = toMilliSec;
}
else
{
// no, how many times do we have to try?
NumberOfWaits = toMilliSec / EachWaitMilliSec;
}
}
for (var idx = 0; idx < NumberOfWaits; idx++)
{
if (handle.WaitOne(EachWaitMilliSec, false))
{
// no timeout, we have data but check cancel first
if ((null != condition))
{
if (condition())
{
throw new ConditionMetException();
}
}
// no, give it to them
return true;
}
// timeout. check for cancel before looping
if ((null != condition))
{
if (condition())
{
throw new ConditionMetException();
}
}
}
// we didn't get anything, return timeout
return false;
}
}
}