How to wait for an IAsyncAction?

RelativeGames picture RelativeGames · Nov 11, 2013 · Viewed 16.1k times · Source

In Windows Store Apps, C++(C# is similar though), doing something like

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
}.wait();

results in an unhandled exception. Usually it's

Microsoft C++ exception: Concurrency::invalid_operation at memory location 0x0531eb58

And I kind of need for that action to finish to get my In App Purchase information before trying to use it. The weird thing here is that anything else besides IAsyncAction waits just fine. IAsyncOperation and IAsyncOperationWithProgress worked just fine, but this ? Exception and then crash.

To be honest, I have no idea what's the difference between an IAsyncOperation and an IAsyncAction, they seem similar to me.

UPDATE :

By analyzing this page http://msdn.microsoft.com/en-us/library/vstudio/hh750082.aspx you can figure out that IAsyncAction is just an IAsyncOperation without a return type. But, you can then see that most IAsyncAction-s are waitable. The real problem though is that certain Windows functions just want to execute on a particular thread (for some reason). ReloadSimulatorAsync is one such fine example.

Using code like this :

void WaitForAsync( IAsyncAction ^A )
{   
    while(A->Status == AsyncStatus::Started)
    {
        std::chrono::milliseconds milis( 1 );
        std::this_thread::sleep_for( milis );
    }   
    AsyncStatus S = A->Status;  
}

results in an infinite loop. If called upon other functions it actually works. The problem here is why does a task need to be executed on a particular thread if everything is Async ? Instead of Async it should be RunOn(Main/UI)Thread or similar.

SOLVED, see answer;

Answer

Adam Maras picture Adam Maras · Nov 12, 2013

Calling wait on the concurrency::task after you create it completely defeats the point of having tasks in the first place.

What you have to realize is that in the Windows Runtime, there are many asynchronous operations that cannot (or should not) be run on (or waited for on) the UI thread; you've found one of them, and now you're trying to wait on it. Instead of potentially causing a deadlock, you're getting an exception.

To remedy this, you need to use a continuation. You're most of the way there; you're already defining a continuation function:

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
}).wait();

// do important things once the simulator has reloaded
important_things();

...but you're not using it. The purpose of the function you pass into then is to be called off the UI thread once the task is complete. So, instead, you should do this:

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
    // do important things once the simulator has reloaded
    important_things();
});

Your important post-reload code won't run until the task is complete, and it will run on a background thread so it doesn't block or deadlock the UI.