PowerShell/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WaitEventCommand.cs

164 lines
4.8 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
using System.Threading;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Waits for a given event to arrive.
/// </summary>
[Cmdlet(VerbsLifecycle.Wait, "Event", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097042")]
[OutputType(typeof(PSEventArgs))]
public class WaitEventCommand : PSCmdlet
{
#region parameters
/// <summary>
/// An identifier for this event subscription.
/// </summary>
[Parameter(Position = 0, ValueFromPipelineByPropertyName = true)]
public string SourceIdentifier
{
get
{
return _sourceIdentifier;
}
set
{
_sourceIdentifier = value;
_matchPattern = WildcardPattern.Get(value, WildcardOptions.IgnoreCase);
}
}
private string _sourceIdentifier = null;
/// <summary>
/// If timeout is specified, the cmdlet will only wait for this number of seconds.
/// Value of -1 means never timeout.
/// </summary>
[Parameter]
[Alias("TimeoutSec")]
[ValidateRangeAttribute(-1, int.MaxValue)]
public int Timeout
{
get
{
return _timeoutInSeconds;
}
set
{
_timeoutInSeconds = value;
}
}
private int _timeoutInSeconds = -1; // -1: infinite, this default is to wait for as long as it takes.
#endregion parameters
private readonly AutoResetEvent _eventArrived = new(false);
private PSEventArgs _receivedEvent = null;
private readonly object _receivedEventLock = new();
private WildcardPattern _matchPattern;
/// <summary>
/// Wait for the event to arrive.
/// </summary>
protected override void ProcessRecord()
{
DateTime startTime = DateTime.UtcNow;
// Subscribe to notification of events received
Events.ReceivedEvents.PSEventReceived += ReceivedEvents_PSEventReceived;
bool received = false;
// Scan the queue to see if it's already arrived
ScanEventQueue();
// And wait for our event handler (or Control-C processor) to give us control
PSLocalEventManager eventManager = (PSLocalEventManager)Events;
while (!received)
{
if (_timeoutInSeconds >= 0)
{
if ((DateTime.UtcNow - startTime).TotalSeconds > _timeoutInSeconds)
break;
}
received = _eventArrived.WaitOne(200);
eventManager.ProcessPendingActions();
}
// Unsubscribe, and write the event information we received
Events.ReceivedEvents.PSEventReceived -= ReceivedEvents_PSEventReceived;
if (_receivedEvent != null)
{
WriteObject(_receivedEvent);
}
}
/// <summary>
/// Handle Control-C.
/// </summary>
protected override void StopProcessing()
{
_eventArrived.Set();
}
private void ReceivedEvents_PSEventReceived(object sender, PSEventArgs e)
{
// If they want to wait on just any event
if (_sourceIdentifier == null)
{
NotifyEvent(e);
}
// They are waiting on a specific one
else
{
ScanEventQueue();
}
}
// Go through all the received events. If one matches the subscription identifier,
// break.
private void ScanEventQueue()
{
lock (Events.ReceivedEvents.SyncRoot)
{
foreach (PSEventArgs eventArg in Events.ReceivedEvents)
{
// If they specified a subscription identifier and we don't match, continue
if ((_matchPattern == null) || (_matchPattern.IsMatch(eventArg.SourceIdentifier)))
{
NotifyEvent(eventArg);
return;
}
}
}
}
// Notify that an event has arrived
private void NotifyEvent(PSEventArgs e)
{
if (_receivedEvent == null)
{
lock (_receivedEventLock)
{
if (_receivedEvent == null)
{
_receivedEvent = e;
_eventArrived.Set();
}
}
}
}
}
}