using System;

/// <summary>
/// Provides a way of testing for whether a certain amount of time has
/// elapsed.
/// </summary>
/// <remarks>
/// <p>This is intended for use in loops which need to do some work until
/// a specific amount of time has elapsed. For example, it can be used
/// to test if a timeout has occurred yet. It can also be used in simple
/// benchmark tests to see how many operations can be performed in a
/// given amount of time.</p>
/// 
/// <p>Its usage is:</p>
/// <code>
/// WaitForTicks wait = new WaitForTicks(TimeSpan.FromSeconds(2));
/// while (!wait.TimeIsUp)
/// {
///     ... do work ...
/// }
/// </code>
/// 
/// <p>This uses the <see cref="Environment.TickCount"/>, which increases
/// monotonically, so it will be unperturbed by changes to the system
/// time. This class deals correctly with wrapping around, both when the
/// tick count rolls from positive to negative, and when it rolls around
/// through 0. However, because the tick count is a 32 bit number, this
/// class cannot wait for more than <see cref="Int32.MaxValue"/>
/// milliseconds.</p>
/// </remarks>
public class WaitForTicks
{
    private readonly int startTicks;
    private readonly uint maxTicks;

    /// <summary>
    /// Create a WaitForTicks object initialized to expire in the
    /// specified amount of time.
    /// </summary>
    /// <param name="time">The amount of time into the future in
    /// which this timer's <see cref="TimeIsUp"/> property will
    /// return true.</param>
    /// <exception cref="ArgumentOutOfRangeException">Thrown if
    /// the specified time is more than <see cref="Int32.MaxValue"/>
    /// millseconds in the future.</exception>
    public WaitForTicks(TimeSpan time)
    {
        double fullMaxTicks = time.TotalMilliseconds;
        if (fullMaxTicks > int.MaxValue)
        {
            // The int.MaxValue limit is a bit arbitrary. In practice,
            // the limit will depend on how often code checks the 
            // TimeIsUp property. Here, they have another
            // int.MaxValue seconds to check it. (They have to
            // check it before the difference rolls round.)
            // You could narrow the margin.  For example,
            // testing for fullMaxTicks > uint.MaxValue - 10000
            // would be OK if you were testing the value at least
            // once every 10 seconds.
            // With the code as it is, there's a safety margin of
            // some 24.5 days!

            throw new ArgumentOutOfRangeException("time", time,
                "Cannot wait for more than Int32.MaxValue milliseconds");
        }

        maxTicks = (uint) fullMaxTicks;
        startTicks = Environment.TickCount;
    }

    /// <summary>
    /// Returns true if the amount of time specified at construction has
    /// elapsed since construction.
    /// </summary>
    public bool TimeIsUp
    {
        get
        {
            uint diff = (uint) (Environment.TickCount - startTicks);
            return diff >= maxTicks;
        }
    }
}

Copyright © 2002-2013, Interact Software Ltd. Please direct all Web site inquiries to webmaster@interact-sw.co.uk