Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1158943
Add 5 InvokeAsync overloads
KirkMunro Oct 17, 2018
ad01380
CodeFactor changes
KirkMunro Oct 17, 2018
db1cb6e
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Nov 4, 2018
f7e1f62
first crack at some C# unit tests
KirkMunro Nov 4, 2018
1d32ec6
codefactor -- added header to file
KirkMunro Nov 4, 2018
e42db00
updated PowerShell member count
KirkMunro Nov 5, 2018
eee1a07
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Nov 15, 2018
d293e43
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Jan 14, 2019
745156b
updated tab completion test
KirkMunro Jan 15, 2019
536db9c
Codacy changes
KirkMunro Feb 15, 2019
ae200cb
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Feb 15, 2019
c800116
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Feb 22, 2019
3b051e8
add default formats for Task class
KirkMunro Feb 26, 2019
561db67
new tests for InvokeAsync methods
KirkMunro Feb 26, 2019
ce40d03
fixed multithreaded test that passes input
KirkMunro Feb 26, 2019
e070175
minor update to latest test
KirkMunro Feb 26, 2019
4b9814e
simplified multithreaded pester tests
KirkMunro Feb 26, 2019
2611869
flushed out more PowerShell tests
KirkMunro Feb 28, 2019
c41450b
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Feb 28, 2019
def3d07
added missing copyright notice
KirkMunro Feb 28, 2019
57b5a76
CodeFactor changes
KirkMunro Feb 28, 2019
2347a84
More CodeFactor changes
KirkMunro Feb 28, 2019
c3ad8f4
refactor delegate and additional tests
KirkMunro Mar 1, 2019
14e91b4
Merge branch 'master' into powershell-invokeasync-methods
KirkMunro Mar 1, 2019
322e6c3
Add lazy delegate initializers and StopAsync method
KirkMunro Mar 1, 2019
429ba36
CodeFactor changes
KirkMunro Mar 1, 2019
b4c2f59
CodeFactor changes
KirkMunro Mar 1, 2019
3d3aff0
review updates, more tests, some refactoring
KirkMunro Mar 1, 2019
006e56b
Codacy changes
KirkMunro Mar 3, 2019
3ad47f3
Updates based on @daxian-dbw's feedback
KirkMunro Mar 3, 2019
af6db95
replace code with Wait-UntilTrue invocation
KirkMunro Mar 3, 2019
2974397
refactor code (replace with Wait-UntilTrue)
KirkMunro Mar 3, 2019
12645db
correction to earlier refactoring of test
KirkMunro Mar 3, 2019
34d4b82
fixes to PowerShell engine async tests
KirkMunro Mar 3, 2019
54cfc7a
fixed incorrect waiting logic in C# tests
KirkMunro Mar 3, 2019
957ec77
more changes in response to PR feedback
KirkMunro Mar 4, 2019
c224fac
added comment to new format views
KirkMunro Mar 4, 2019
298cfe0
PR review change
KirkMunro Mar 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ internal static IEnumerable<ExtendedTypeDefinition> GetFormatData()
yield return new ExtendedTypeDefinition(
"Microsoft.Management.Infrastructure.CimInstance#__PartialCIMInstance",
ViewsOf_Microsoft_Management_Infrastructure_CimInstance___PartialCIMInstance());

yield return new ExtendedTypeDefinition(
"System.Threading.Tasks.Task",
ViewsOf_System_Threading_Tasks_Task());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_System_CodeDom_Compiler_CompilerError()
Expand Down Expand Up @@ -1709,5 +1713,51 @@ private static IEnumerable<FormatViewDefinition> ViewsOf_Microsoft_Management_In
.EndEntry()
.EndControl());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_System_Threading_Tasks_Task()
{
// Avoid referencing the Result property in these views to avoid potential
// deadlocks that may occur. Result should only be referenced once the task
// is actually completed.
yield return new FormatViewDefinition(
"System.Threading.Tasks.Task",
TableControl
.Create()
.AddHeader(label: "Id")
.AddHeader(label: "IsCompleted")
.AddHeader(label: "Status")
.StartRowDefinition()
.AddPropertyColumn("Id")
.AddPropertyColumn("IsCompleted")
.AddPropertyColumn("Status")
.EndRowDefinition()
.EndTable());

yield return new FormatViewDefinition(
"System.Threading.Tasks.Task",
ListControl
.Create()
.StartEntry()
.AddItemProperty(@"AsyncState")
.AddItemProperty(@"AsyncWaitHandle")
.AddItemProperty(@"CompletedSynchronously")
.AddItemProperty(@"CreationOptions")
.AddItemProperty(@"Exception")
.AddItemProperty(@"Id")
.AddItemProperty(@"IsCanceled")
.AddItemProperty(@"IsCompleted")
.AddItemProperty(@"IsCompletedSuccessfully")
.AddItemProperty(@"IsFaulted")
.AddItemScriptBlock(
@"
if ($_.IsCompleted) {
$_.Result
}
",
label: "Result")
.AddItemProperty(@"Status")
.EndEntry()
.EndList());
}
}
}
259 changes: 259 additions & 0 deletions src/System.Management.Automation/engine/hostifaces/PowerShell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Management.Automation.Runspaces.Internal;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Management.Infrastructure;
using Dbg = System.Management.Automation.Diagnostics;

Expand Down Expand Up @@ -598,6 +599,10 @@ public sealed class PowerShell : IDisposable
private bool _isBatching = false;
private bool _stopBatchExecution = false;

// Delegates for asynchronous invocation/termination of PowerShell commands
private readonly Func<IAsyncResult, PSDataCollection<PSObject>> _endInvokeMethod;
private readonly Action<IAsyncResult> _endStopMethod;

#endregion

#region Internal Constructors
Expand Down Expand Up @@ -633,6 +638,8 @@ private PowerShell(PSCommand command, Collection<PSCommand> extraCommands, objec
ErrorBufferOwner = true;
InformationalBuffers = new PSInformationalBuffers(InstanceId);
Streams = new PSDataStreams(this);
_endInvokeMethod = EndInvoke;
_endStopMethod = EndStop;
}

/// <summary>
Expand Down Expand Up @@ -695,6 +702,9 @@ internal PowerShell(ObjectStreamBase inputstream,
{
RemotePowerShell = new ClientRemotePowerShell(this, runspacePool.RemoteRunspacePoolInternal);
}

_endInvokeMethod = EndInvoke;
_endStopMethod = EndStop;
}

/// <summary>
Expand Down Expand Up @@ -3057,6 +3067,226 @@ public IAsyncResult BeginInvoke<TInput, TOutput>(PSDataCollection<TInput> input,
return CoreInvokeAsync<TInput, TOutput>(input, output, settings, callback, state, null);
}

/// <summary>
/// Invoke a PowerShell command asynchronously.
/// Use await to wait for the command to complete and obtain the output of the command.
/// </summary>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Cannot perform the operation because the command is already started.
/// Stop the command and try the operation again.
/// (or)
/// No command is added.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task<PSDataCollection<PSObject>> InvokeAsync()
{
return await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke(), _endInvokeMethod).ConfigureAwait(false);
}

/// <summary>
/// Invoke a PowerShell command asynchronously.
/// Use await to wait for the command to complete and obtain the output of the command.
/// </summary>
/// <remarks>
/// <para>
/// When invoked using InvokeAsync, invocation doesn't
/// finish until Input is closed. Caller of InvokeAsync must
/// close the input buffer after all input has been written to
/// input buffer. Input buffer is closed by calling
/// Close() method.
/// </para><para>
/// If you want this command to execute as a standalone cmdlet
/// (that is, using command-line parameters only),
/// be sure to call Close() before calling InvokeAsync(). Otherwise,
/// the command will be executed as though it had external input.
/// If you observe that the command isn't doing anything,
/// this may be the reason.
/// </para>
/// </remarks>
/// <typeparam name="T">
/// Type of the input buffer.
/// </typeparam>
/// <param name="input">
/// Input to the command. See remarks for more details.
/// </param>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Cannot perform the operation because the command is already started.
/// Stop the command and try the operation again.
/// (or)
/// No command is added.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task<PSDataCollection<PSObject>> InvokeAsync<T>(PSDataCollection<T> input)
=> await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke<T>(input), _endInvokeMethod).ConfigureAwait(false);

/// <summary>
/// Invoke a PowerShell command asynchronously.
/// Use await to wait for the command to complete and obtain the output of the command.
/// </summary>
/// <remarks>
/// <para>
/// When invoked using InvokeAsync, invocation doesn't
/// finish until Input is closed. Caller of InvokeAsync must
/// close the input buffer after all input has been written to
/// input buffer. Input buffer is closed by calling
/// Close() method.
/// </para><para>
/// If you want this command to execute as a standalone cmdlet
/// (that is, using command-line parameters only),
/// be sure to call Close() before calling InvokeAsync(). Otherwise,
/// the command will be executed as though it had external input.
/// If you observe that the command isn't doing anything,
/// this may be the reason.
/// </para>
/// </remarks>
/// <typeparam name="T">
/// Type of the input buffer.
/// </typeparam>
/// <param name="input">
/// Input to the command. See remarks for more details.
/// </param>
/// <param name="settings">
/// Invocation Settings.
/// </param>
/// <param name="callback">
/// An AsyncCallback to call once the command is invoked.
/// </param>
/// <param name="state">
/// A user supplied state to call the <paramref name="callback"/>
/// with.
/// </param>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Cannot perform the operation because the command is already started.
/// Stop the command and try the operation again.
/// (or)
/// No command is added.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task<PSDataCollection<PSObject>> InvokeAsync<T>(PSDataCollection<T> input, PSInvocationSettings settings, AsyncCallback callback, object state)
=> await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke<T>(input, settings, callback, state), _endInvokeMethod).ConfigureAwait(false);

/// <summary>
/// Invoke a PowerShell command asynchronously.
/// Use await to wait for the command to complete and obtain the output of the command.
/// </summary>
/// <remarks>
/// <para>
/// When invoked using InvokeAsync, invocation doesn't
/// finish until Input is closed. Caller of InvokeAsync must
/// close the input buffer after all input has been written to
/// input buffer. Input buffer is closed by calling
/// Close() method.
/// </para><para>
/// If you want this command to execute as a standalone cmdlet
/// (that is, using command-line parameters only),
/// be sure to call Close() before calling InvokeAsync(). Otherwise,
/// the command will be executed as though it had external input.
/// If you observe that the command isn't doing anything,
/// this may be the reason.
/// </para>
/// </remarks>
/// <typeparam name="TInput">
/// Type of input object(s) for the command invocation.
/// </typeparam>
/// <typeparam name="TOutput">
/// Type of output object(s) expected from the command invocation.
/// </typeparam>
/// <param name="input">
/// Input to the command. See remarks for more details.
/// </param>
/// <param name="output">
/// A buffer supplied by the user where output is collected.
/// </param>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke,
/// or null if the caller provided their own buffer.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Cannot perform the operation because the command is already started.
/// Stop the command and try the operation again.
/// (or)
/// No command is added.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task<PSDataCollection<PSObject>> InvokeAsync<TInput, TOutput>(PSDataCollection<TInput> input, PSDataCollection<TOutput> output)
=> await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke<TInput, TOutput>(input, output), _endInvokeMethod).ConfigureAwait(false);

/// <summary>
/// Invoke a PowerShell command asynchronously and collect
/// output data into the buffer <paramref name="output"/>.
/// Use await to wait for the command to complete and obtain the output of the command.
/// </summary>
/// <remarks>
/// <para>
/// When invoked using InvokeAsync, invocation doesn't
/// finish until Input is closed. Caller of InvokeAsync must
/// close the input buffer after all input has been written to
/// input buffer. Input buffer is closed by calling
/// Close() method.
/// </para><para>
/// If you want this command to execute as a standalone cmdlet
/// (that is, using command-line parameters only),
/// be sure to call Close() before calling InvokeAsync(). Otherwise,
/// the command will be executed as though it had external input.
/// If you observe that the command isn't doing anything,
/// this may be the reason.
/// </para>
/// </remarks>
/// <typeparam name="TInput">
/// Type of input object(s) for the command invocation.
/// </typeparam>
/// <typeparam name="TOutput">
/// Type of output object(s) expected from the command invocation.
/// </typeparam>
/// <param name="input">
/// Input to the command. See remarks for more details.
/// </param>
/// <param name="output">
/// A buffer supplied by the user where output is collected.
/// </param>
/// <param name="settings">
/// Invocation Settings.
/// </param>
/// <param name="callback">
/// An AsyncCallback to call once the command is invoked.
/// </param>
/// <param name="state">
/// A user supplied state to call the <paramref name="callback"/>
/// with.
/// </param>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke,
/// or null if the caller provided their own buffer.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Cannot perform the operation because the command is already started.
/// Stop the command and try the operation again.
/// (or)
/// No command is added.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task<PSDataCollection<PSObject>> InvokeAsync<TInput, TOutput>(PSDataCollection<TInput> input, PSDataCollection<TOutput> output, PSInvocationSettings settings, AsyncCallback callback, object state)
=> await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke<TInput, TOutput>(input, output, settings, callback, state), _endInvokeMethod).ConfigureAwait(false);

/// <summary>
/// Begins a batch execution.
/// </summary>
Expand Down Expand Up @@ -3554,6 +3784,35 @@ public void EndStop(IAsyncResult asyncResult)
ResetOutputBufferAsNeeded();
}

/// <summary>
/// Stop a PowerShell command asynchronously.
/// Use await to wait for the command to stop.
/// </summary>
/// <remarks>
/// <para>
/// If the command is not started, the state of the PowerShell instance
/// is changed to Stopped and corresponding events will be raised.
/// </para>
/// </remarks>
/// <param name="callback">
/// An AsyncCallback to call once the command is invoked.
/// </param>
/// <param name="state">
/// A user supplied state to call the <paramref name="callback"/>
/// with.
/// </param>
/// <returns>
/// The output buffer created to hold the results of the asynchronous invoke,
/// or null if the caller provided their own buffer.
/// </returns>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public async Task StopAsync(AsyncCallback callback, object state)
{
await Task.Factory.FromAsync(BeginStop(callback, state), _endStopMethod).ConfigureAwait(false);
}

#endregion

#region Event Handlers
Expand Down
2 changes: 1 addition & 1 deletion test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ dir -Recurse `
It "Test member completion of a static method invocation" {
$inputStr = '[powershell]::Create().'
$res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length
$res.CompletionMatches | Should -HaveCount 32
$res.CompletionMatches | Should -HaveCount 34
$res.CompletionMatches[0].CompletionText | Should -BeExactly "Commands"
}
}
Expand Down
Loading