Skip to content

Conversation

@daxian-dbw
Copy link
Member

@daxian-dbw daxian-dbw commented Mar 6, 2019

PR Summary

Update the task-based async APIs added to PowerShell to not use the aysnc/await keywords, but to return a Task object directly.
There is nothing to continue on after the Task.Factory.FromAsync call in those methods, so there is not need to use aysnc and await keywords, which turns the method into a state machine class.

The behavior of the API is slightly different comparing with before this change.
But note that, the behavior when calling the async APIs with await is the same -- exception will be thrown at await.

Before this change:

Calling InvokeAsync() without await will always return a task even if the BeginInvoke call, which gets evaluated before calling FromAsync, throws an exception.
This is because with async/await, this method is written by compiler with a state machine, where all exceptions are caught can passed to the task that is going to be returned.

After this change:

Calling InvokeAsync() without await will throw exception if the BeginInovke call throws exception. So it fails early.

/cc @KirkMunro

PR Context

PR Checklist

@daxian-dbw daxian-dbw self-assigned this Mar 6, 2019
@daxian-dbw daxian-dbw requested a review from BrucePay as a code owner March 6, 2019 18:39
@daxian-dbw daxian-dbw changed the title Update the task-based async APIs added to PowerShell to return a Task object directly WIP: Update the task-based async APIs added to PowerShell to return a Task object directly Mar 6, 2019
@daxian-dbw daxian-dbw changed the title WIP: Update the task-based async APIs added to PowerShell to return a Task object directly Update the task-based async APIs added to PowerShell to return a Task object directly Mar 6, 2019
Copy link
Collaborator

@rjmholt rjmholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes sense to me. The InvokeAsync method itself shouldn't be async, since it just wraps another asynchronous invocation. Therefore it should synchronously pass the handle back to the caller. Everything looks good -- just want to discuss the recommended overload to see if we can improve our usage of that.

return await Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke(), _endInvokeMethod).ConfigureAwait(false);
}
public Task<PSDataCollection<PSObject>> InvokeAsync()
=> Task<PSDataCollection<PSObject>>.Factory.FromAsync(BeginInvoke(), _endInvokeMethod);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing I'm curious about is that the documentation here says that the FromAsync overload taking an IAsyncResult rather than a Func<AsyncCallback, object, IAsyncResult> is slower.

I tried looking into this, and the doc written for wrapping the old API with the new one here, but I'm not quite sure what needs to be done to turn the BeginInvoke() method we have into the appropriate callback handler

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an SO post that touches on this question

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's another one that has some more detail

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, after reading a bit more, it looks like the callback functionality would need to be supported by PowerShell itself and passed into a BeginInvoke() method we provide so that it can signal that the task is done.

I was proceeding under the impression that it might be possible to fudge that with a wrapping lambda based on the documentation's recommendation of "use this overload", but I'm guessing that's wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, our Begin/End implementation doesn't strictly conform to the APM contract.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will improve?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason to update the existing APM APIs.

Copy link
Collaborator

@iSazonov iSazonov Mar 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was not addressed comment #8056 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can defer opening that issue until we get some real feedback on those async APIs that request performance enhancement.
Bruce brought that up mainly because when it was discussed in the committee meeting, there was an ask about if we should have separate implementation for the task-based APIs.

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 7, 2019

@daxian-dbw With the PR we get a possibility to use the methods in scripts, yes? So we need to update related doc issue.

@daxian-dbw
Copy link
Member Author

daxian-dbw commented Mar 7, 2019

Scripts can use those new async APIs. They are tested in Pester. APIs are documented by its xml doc comments, right? I guess nothing needs to be done in the powershell-doc repo. @adityapatwardhan can you please confirm?

@daxian-dbw daxian-dbw added the CL-Engine Indicates that a PR should be marked as an engine change in the Change Log label Mar 7, 2019
@daxian-dbw daxian-dbw merged commit 50f3e7f into PowerShell:master Mar 7, 2019
@daxian-dbw daxian-dbw deleted the asyncAPI branch March 7, 2019 19:24
@TravisEz13 TravisEz13 added this to the 6.2.0 milestone Mar 7, 2019
TravisEz13 added a commit to TravisEz13/PowerShell that referenced this pull request Mar 7, 2019
… object directly (PowerShell#9079)

Update the task-based async APIs added to PowerShell to not use the `aysnc/await` keywords, but to return a `Task` object directly.
There is nothing to continue on after the `Task.Factory.FromAsync` call in those methods, so there is not need to use `aysnc` and `await` keywords, which turns the method into a state machine class unnecessarily.
# Conflicts:
#	src/System.Management.Automation/engine/hostifaces/PowerShell.cs
#	test/powershell/engine/Api/TaskBasedAsyncPowerShellAPI.Tests.ps1
TravisEz13 pushed a commit that referenced this pull request Mar 13, 2019
… object directly (#9079)

Update the task-based async APIs added to PowerShell to not use the `aysnc/await` keywords, but to return a `Task` object directly.
There is nothing to continue on after the `Task.Factory.FromAsync` call in those methods, so there is not need to use `aysnc` and `await` keywords, which turns the method into a state machine class unnecessarily.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-Engine Indicates that a PR should be marked as an engine change in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants