microsoft/cppwinrt

Best way to keep track of pending IAsyncActions

Closed this issue · 4 comments

Version

No response

Summary

This isn't a bug. Just a question.

Let's say you have a long-running application that fires off a bunch of methods on the threadpool using winrt::resume_background(). And then when it's time to close the app, you'd like to wait for any in-flight tasks to finish up.

I understand that there are functions like winrt::when_all that allow you to wait on a bunch of IAsyncActions. Or you can loop through a vector of IAsyncActions and co_await them, as discussed here.

But my question is: what's the best strategy for keeping a hold on your pending IAsyncActions so that you know the ones to wait on when it's time to quit? Is there a specific class or function for that?

Granted, you could just store each and every IAsyncAction in a vector, but for a long-running app that may fire off hundreds or thousands of tasks, it seems like that vector would get really large and inefficient--especially since, as a practical matter, there are probably only one or two outstanding tasks to wait on when it's time to quit.

Perhaps there could be a class that acts like a vector for storing IAsyncActions, except it would remove each one upon completion. But that would monopolize the IAsyncAction's delegate, wouldn't it?

Or is it better to use a latch (i.e., reverse semaphore) in this situation?

Thanks for any information or guidance.

Reproducible example

No response

Expected behavior

No response

Actual behavior

No response

Additional comments

No response

I have been doing the following:

std::mutex m_asyncSetLock;
std::unordered_set<winrt::Windows::Foundation::IAsyncAction> m_inflightAsync;

void Foo::registerInflightAsync(winrt::Windows::Foundation::IAsyncAction async)
{
    async.Completed({ this, &Foo::unregisterInflightAsync });

    std::scoped_lock guard(m_asyncSetLock);
    m_inflightAsync.insert(std::move(async));
}

void Foo::unregisterInflightAsync(const winrt::Windows::Foundation::IAsyncAction& async, winrt::Windows::Foundation::AsyncStatus status)
{
    std::scoped_lock guard(m_asyncSetLock);
    m_inflightAsync.erase(async);
}

It does, as you say, monopolize the Completed handler.

Thanks a lot. So then how do you use it at the end of an application if you can’t co_await the actions?

At the end of the application, I just hard-block with .get() on each action consecutively.

Oh yeah, that makes sense. Thanks again.