Don't destroy the IAsyncInfo from inside Completed handler #671
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
If the Completed handler runs synchronously, that would cause us to destroy the operation while a method on the operation is still running. That's not very nice.
This stems from the fact that synchronous completion causes the coroutine to resume before put_Completed returns. Resuming the coroutine causes it to get the result and then destroy the async operation. If the Completed handler is called synchronously from within put_Completed (which will happen if the operation has already completed), the operation will be destroyed while the put_Completed call is still outstanding. To avoid this problem, await_suspend extends the lifetime of the async operation across the call to put_Completed, so that the synchronous completion doesn't destroy the async operation prematurely.
If the operation completes asynchronously, then the destruction happens at coroutine resumption, which is fine. The put_Completed method returned a long time ago.
We make a copy of the async operation to ensure it is not destroyed while put_Completed is running. This costs us an AddRef/Release pair, but avoiding the AddRef/Release will cost us a few atomic operations (some of which require memory barriers), so it's basically a wash.
A more advanced solution would steal the async operation into await_suspend, and then use the IAsyncAction/IAsyncOperation passed to the completion handler to get the results. We'll use the simple solution for now.