Files
DP44/Common/DTS.Common.Utilities/WaitWithCondition.cs
2026-04-17 14:55:32 -04:00

135 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DTS.Common.Utilities
{
/// <summary>
/// this is a class to simplify waiting for a resource
/// it can wait for a specified period and also check another signal or condition
/// </summary>
public class WaitWithCondition
{
/// <summary>
/// the max time to wait in any spin cycle
/// </summary>
/// <remarks>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
/// </remarks>
public static int EachWaitMilliSec = 20; // we wait 0.02 second each time
/// <summary>
/// optional function to check while waiting for handle to return.
/// </summary>
/// <returns>bool if condition has been met, false otherwise</returns>
public delegate bool Condition();
/// <summary>
/// this exception is used to indicate a condition was met to the calling code
/// </summary>
public class ConditionMetException : ApplicationException
{
}
/// <summary>
/// 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
/// </summary>
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();
}
/// <summary>
/// wait for a resource to be available
/// </summary>
/// <param name="handle"><see cref="System.Threading.WaitHandle" /> object to wait on</param>
/// <param name="toMilliSec">max amount of time in milliseconds to wait
/// use <see cref="System.Threading.Timeout.Infinite" /> to wait for the max amount of time
/// allowed.
/// </param>
/// <param name="condition">an additional condition to check. Can be null.
/// if condition is met before resource is available a <see cref="WaitWithCondition.ConditionMetException" />
/// is thrown.
/// </param>
/// <returns>true indicates the <see cref="System.Threading.WaitHandle" /> object has returned within
/// the requested time period, false otherwise
/// </returns>
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;
}
}
}