Search code examples
winapiwindows-runtimewinrt-async

IAsyncOperation "Completed" handler race conditions?


The IAsyncOperation<TResult> interface exposes a Completed property, which users can attach methods to that are invoked when the operation completes.

MS has example code online that illustrates the usage (I've trimmed it down for the purposes of this question):

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Syndication.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

void ProcessFeedAsync()
{
    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;

    auto async_op_with_progress = syndicationClient.RetrieveFeedAsync(rssFeedUri);
    async_op_with_progress.Completed(
        [](
            IAsyncOperationWithProgress<SyndicationFeed,
            RetrievalProgress> const& sender,
            AsyncStatus const /* asyncStatus */)
        {
            SyndicationFeed syndicationFeed = sender.GetResults();
            // use syndicationFeed;
        });
}

The intended usage pattern seems to be:

  1. Invoke the asynchronous operation
  2. Capture the operation token into a local variable
  3. Attach a completion function (here a C++ lambda) to the "Completed" property.

Are there any safeguards against the race condition where the asynchronous operation completes sometime after step 1 but before step 3? It's unlikely to happen with the provided example, because it's fetching something from the internet, but there are plenty of SDK calls that simply queue up data and return very quickly.

Simply guarding the Completed assignment with a check for Status() != AsyncStatus::Completed narrows the race-condition window but doesn't fundamentally fix the issue, if one actually exists. (e.g. If IAsyncOperation is guaranteed to call the handler immediately at Completed-assignment-time if the operation has already completed, then there's no issue).

The C++/WinRT C++17 projection does include support for native coroutines, but the programming model I'm locked into can't support that technique, and I have to use the Completed property. (Also, my example is C++/WinRT but it seems like this exists in the C# projection as well?)


Solution

  • Synthesized answer and explanation from Raymond Chen's comment:

    If the user attaches a method to the Completed property after the IAsyncOperation has already completed, the attached method will still be called. There are no guarantees about when it will be called, based on policies adhered to by the object (and probably things like the apartment model). It may be immediate, or it may be in the future, but the method will eventually be scheduled for execution.