Libs/CancelableWait/src/StartCancelableWaitCommand.cs

using System;
using System.Management.Automation;
using System.Threading;
 
/// <summary>
/// Suspend shell, script, or runspace activity for the specified period of time.
/// </summary>
[Cmdlet(VerbsLifecycle.Start, "CancelableWait", DefaultParameterSetName = "Seconds")]
public sealed class StartCancelableWaitCommand : PSCmdlet, IDisposable
{
    private bool _disposed = false;
 
    #region IDisposable
    /// <summary>
    /// Dispose method of IDisposable interface.
    /// </summary>
    public void Dispose()
    {
        if (!_disposed)
        {
            if (_waitHandle != null)
            {
                _waitHandle.Dispose();
                _waitHandle = null;
            }
 
            _disposed = true;
        }
    }
 
    #endregion
 
    #region parameters
 
    /// <summary>
    /// Allows sleep time to be specified in seconds.
    /// </summary>
    [Parameter(Position = 0, Mandatory = true, ParameterSetName = "Seconds", ValueFromPipeline = true,
               ValueFromPipelineByPropertyName = true)]
    [ValidateRangeAttribute(0.0, (double)(int.MaxValue / 1000))]
    public double Seconds { get; set; }
 
    /// <summary>
    /// Allows sleep time to be specified in milliseconds.
    /// </summary>
    [Parameter(Mandatory = true, ParameterSetName = "Milliseconds", ValueFromPipelineByPropertyName = true)]
    [ValidateRangeAttribute(0, int.MaxValue)]
    [Alias("ms")]
    public int Milliseconds { get; set; }
 
    [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)]
    public CancellationToken CancellationToken { get; set; }
 
    #endregion
 
    #region methods
 
    // Wait handle which is used by thread to sleep.
    private ManualResetEventSlim _waitHandle;
 
    // object used for synchronizes pipeline thread and stop thread
    // access to waitHandle
    private readonly object _syncObject = new();
 
    // this is set to true by stopProcessing
    private bool _stopping = false;
 
    /// <summary>
    /// This method causes calling thread to sleep for specified milliseconds.
    /// </summary>
    private void Sleep(int milliSecondsToSleep)
    {
        lock (_syncObject)
        {
            if (!_stopping)
            {
                _waitHandle = new ManualResetEventSlim(false);
            }
        }
 
        try
        {
            _waitHandle?.Wait(milliSecondsToSleep, CancellationToken);
        }
        catch (OperationCanceledException operationcanceled)
        {
            ErrorRecord er = new(operationcanceled, "OperationCanceledException", ErrorCategory.OperationTimeout, null);
            WriteError(er);
        }
    }
 
    /// <summary>
    /// ProcessRecord method.
    /// </summary>
    protected override void ProcessRecord()
    {
        int sleepTime = 0;
 
        switch (ParameterSetName)
        {
            case "Seconds":
                sleepTime = (int)(Seconds * 1000);
                break;
 
            case "Milliseconds":
                sleepTime = Milliseconds;
                break;
 
            default:
                break;
        }
 
        Sleep(sleepTime);
    }
 
    /// <summary>
    /// StopProcessing override.
    /// </summary>
    protected override void StopProcessing()
    {
        lock (_syncObject)
        {
            _stopping = true;
            _waitHandle?.Set();
        }
    }
 
    #endregion
}