14

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;

RelativeGames
  • 1,593
  • 2
  • 18
  • 27
  • 2
    I believe IAsyncOperation returns a result and IAsyncAction does not. I don't know if IAsyncAction is awaitable or not, truth be told. In C#, awaitable async methods return type Task or Task. Can you await `Action`? I don't know if C++ has async/await. – Nate Diamond Nov 11 '13 at 23:16
  • I'm waiting with the "}.wait();" part, that's the await C# equivalent, so yes IAsyncAction is awaitable. One other weird thing is that instead of create_task I can call task and has the same effect of throwing the exception. – RelativeGames Nov 11 '13 at 23:44
  • Just a note: the `task::wait` is not the equivalent to C#'s `await` keyword. `await` causes the compiler to restructure the code into continuations behind the scenes. – Adam Maras Nov 12 '13 at 19:36
  • Got any reference for that ? I think that would make things extremely hard to follow. MessageDialog::ShowAsync (for example) is only callable from the UI Thread. In the scenario you're describing if it executes on a background thread when you think you are on the UI thread, it will cause a crash/exception. – RelativeGames Nov 14 '13 at 11:56
  • To C# programmers looking for GetAwaiter() on IAsyncAction - you need to add `using System;` and a reference to `System.Runtime.WindowsRuntime.dll` from c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\ or similar. – Slate Jan 26 '16 at 15:55

4 Answers4

6

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.

Adam Maras
  • 26,269
  • 6
  • 65
  • 91
  • I guess you're right. If there was a ReloadSimulatorSync function I'd call that. Unfortunately, in Windows 8 Runtime everything is Async. My problem with your solution is that 1) I'm using an #ifdef USE_STORE_SIMULATOR, so all that code and 2 other tasks in tasks are called ONLY if using a simulator, the code becomes rather unreadable on the #else part. 2) The code that depends on this is inside other classes, I'd have to move a dozen classes/functions just to fit into this async paradigm. It's annoying when your entire program is a huge chain of continuations. – RelativeGames Nov 11 '13 at 23:46
  • You're right, it's a big leap coming from synchronous programming. But, unfortunately for your code, the platform is built in a manner that requires you to consider asynchronicity. In turn, that's going to cause you to propogate asynchronous-friendly code throughout your codebase. Microsoft saw this coming, which is why they baked the `async` and `await` operators into C#, to make this sort of refactoring easier. Unfortunately, that ease can't be replicated in C++/CX. – Adam Maras Nov 12 '13 at 19:40
  • I wouldn't say "saw this coming". I'd say they thought about the easiest way to use multiple CPU cores without writing specific multithreaded code for devices with 2+ cores. And does this make Windows 8/RT faster than other platforms ? Not really. Does this annoy developers ? Yes. One thing I'd point out is that it completely trashes any CPU caching in simple functions, and most of the time continuations/tasks are pretty short. – RelativeGames Nov 13 '13 at 13:35
  • I'd argue that letting the Windows Runtime execute the asynchronous continuation on an already-available background thread (like an I/O completion port) negates any performance hit you might see from a cache miss. – Adam Maras Nov 13 '13 at 17:37
6

This is the magical fix that gets the job done :

void WaitForAsync( IAsyncAction ^A )
{   
    while(A->Status == Windows::Foundation::AsyncStatus::Started)
    {   
        CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);                     
    }

    Windows::Foundation::AsyncStatus S = A->Status; 
}
RelativeGames
  • 1,593
  • 2
  • 18
  • 27
  • Is it safety to make nested call to `ProcessEvents()`? The first one called us, and the second one is called by us. – Alex P. Jul 24 '17 at 21:30
2

In general you should use the continuations (.then(...)), like Adam's answer says, and not block. But lets say you want to do a wait for some reason (for testing some code?), you can trigger an event from the last continuation (to use C# parlance):

    TEST_METHOD(AsyncOnThreadPoolUsingEvent)
    {

        std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();


        int i;

        auto workItem = ref new WorkItemHandler(
            [_completed, &i](Windows::Foundation::IAsyncAction^ workItem)
        {

            Windows::Storage::StorageFolder^ _picturesLibrary = Windows::Storage::KnownFolders::PicturesLibrary;

            Concurrency::task<Windows::Storage::StorageFile^> _getFileObjectTask(_picturesLibrary->GetFileAsync(L"art.bmp"));

            auto _task2 = _getFileObjectTask.then([_completed, &i](Windows::Storage::StorageFile^ file)
            {

                i = 90210;

                _completed->set();

            });

        });

        auto asyncAction = ThreadPool::RunAsync(workItem);

        _completed->wait();


        int j = i;

    }

By the way, for some reason this method causes an exception when after it is over in visual studio tests. I've tested it in an app too though and it worked with no problem. I'm not quite sure what the problem is with the test.

If anybody wants a C++/Wrl example then I have that too.

Update 07/08/2017: As requested here is a C++/Wrl example. I have just run this in a Universal Windows (10) Test project in Visual Studio 2017. The key thing here is the weird part Callback<Implements< RuntimeClassFlags<ClassicCom >, IWorkItemHandler , FtmBase >> , as opposed to just Callback<IWorkItemHandler> . When I had the latter, the program jammmed except for when it was in a .exe project. I found this solution here: https://social.msdn.microsoft.com/Forums/windowsapps/en-US/ef6f84f6-ad4d-44f0-a107-3922d56662e6/thread-pool-task-blocking-ui-thread . See "agile objects" for more information.

#include "pch.h"
#include "CppUnitTest.h"
#include <Windows.Foundation.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>


using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Windows::System::Threading;

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;



namespace TestWinRtAsync10
{
    TEST_CLASS(UnitTest1)
    {
    public:

        TEST_METHOD(AsyncOnThreadPoolUsingEvent10Wrl)
        {
            HRESULT hr = BasicThreadpoolTestWithAgileCallback();

            Assert::AreEqual(hr, S_OK);
        }

        HRESULT BasicThreadpoolTestWithAgileCallback()
        {

            std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();

            ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> _threadPool;
            HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &_threadPool);

            ComPtr<IAsyncAction> asyncAction;
            hr = _threadPool->RunAsync(Callback<Implements<RuntimeClassFlags<ClassicCom>, IWorkItemHandler, FtmBase>>([&_completed](IAsyncAction* asyncAction) -> HRESULT
            {

                // Prints a message in debug run of this test
                std::ostringstream ss;
                ss << "Threadpool work item running.\n";
                std::string _string = ss.str();
                std::wstring stemp = std::wstring(_string.begin(), _string.end());
                OutputDebugString(stemp.c_str());
                // 
                _completed->set();

                return S_OK;

            }).Get(), &asyncAction);

            _completed->wait();

            return S_OK;
        }

    };
}

Update 08/08/2017: More example, per the comments.

#include "pch.h"
#include "CppUnitTest.h"

#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>


#include <Windows.ApplicationModel.Core.h>

using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;

namespace TestWinRtAsync10
{
    TEST_CLASS(TestWinRtAsync_WrlAsyncTesting)
    {

    public:

        TEST_METHOD(PackageClassTest)
        {
            ComPtr<ABI::Windows::ApplicationModel::IPackageStatics> _pPackageStatics;

            HRESULT hr = GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &_pPackageStatics);

            ComPtr<ABI::Windows::ApplicationModel::IPackage> _pIPackage;

            hr = _pPackageStatics->get_Current(&_pIPackage);

            ComPtr<ABI::Windows::ApplicationModel::IPackage3> _pIPackage3;

            hr = _pIPackage->QueryInterface(__uuidof(ABI::Windows::ApplicationModel::IPackage3), &_pIPackage3);

            ComPtr<__FIAsyncOperation_1___FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry> _pAsyncOperation;

            hr = _pIPackage3->GetAppListEntriesAsync(&_pAsyncOperation);

            std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();

            _pAsyncOperation->put_Completed(Microsoft::WRL::Callback<Implements<RuntimeClassFlags<ClassicCom>, ABI::Windows::Foundation::IAsyncOperationCompletedHandler <__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>, FtmBase >>
                ([&_completed](ABI::Windows::Foundation::IAsyncOperation<__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>* pHandler, AsyncStatus status)
            {
                __FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry* _pResults = nullptr;

                HRESULT hr = pHandler->GetResults(&_pResults);

                ComPtr<ABI::Windows::ApplicationModel::Core::IAppListEntry> _pIAppListEntry;

                unsigned int _actual;

                hr = _pResults->GetMany(0, 1, &_pIAppListEntry, &_actual);

                ComPtr<ABI::Windows::ApplicationModel::IAppDisplayInfo> _pDisplayInfo;

                hr = _pIAppListEntry->get_DisplayInfo(&_pDisplayInfo);

                Microsoft::WRL::Wrappers::HString _HStrDisplayName;

                hr =  _pDisplayInfo->get_DisplayName(_HStrDisplayName.GetAddressOf());


                const wchar_t * _pWchar_displayName = _HStrDisplayName.GetRawBuffer(&_actual);


                OutputDebugString(_pWchar_displayName);


                _completed->set();

                return hr;



            }).Get());

            _completed->wait();

        };

    };
}

This outputted:

TestWinRtAsync10

Elliot
  • 2,002
  • 1
  • 20
  • 20
  • Hi, Can you share the C++/Wrl example that you mentioned in your answer? – Sahil Singh Aug 04 '17 at 09:11
  • @SahilSingh Done. – Elliot Aug 07 '17 at 12:41
  • As I understand for `IAgileObject` your code uses `FtmBase`, but the problem is that `__FIVectorView_1_Windows__CApplicationModel__CCore__CAppList‌​Entry_t`, the class I am using to get data from a WinRT API (`GetAppListEntriesAsync()` from `package` class), doesn't implement `IInspectable`, thus Visual Studio throws the following error. `Error C2338 'I' has to derive from 'IWeakReference', 'IWeakReferenceSource' or 'IInspectable' AsyncTask c:\sw\tools\sdk\winsdk\win10\include\winrt\wrl\implements.h 413` – Sahil Singh Aug 07 '17 at 13:14
  • @SahilSingh I don't follow your last comment, so I tried using the classes & API that you mentioned and added another example. I muddled through with heavy use of intellisense and it's working. BTW all Windows Runtime interfaces inherit from the IInspectable interface. – Elliot Aug 08 '17 at 09:26
1

Just in case anyone needs here is solution in C++/WinRT. Say you have function ProcessFeedAsync() that would return IAsyncAction, just need following simple code:

winrt::init_apartment();

auto processOp{ ProcessFeedAsync() };
// do other work while the feed is being printed.
processOp.get(); // no more work to do; call get() so that we see the printout before the application exits.

source

miradham
  • 2,285
  • 16
  • 26