locked
Wrap Async operation In WinRT Component, ... and get exception in Metro App?

    Question

  • Hi friends, I 've got a question about async operation here.
    I wrapped a simple async op in a WinRT component, in the method I use task + lambda to perform async op,
    I tried to use task<T>::get() to block the method till the value is obtained.
    But when I called the method from Metro App, I got unexpected exception.
    Is there sth. wrong in my approach, and, if any, how could I achieve my goal?

    int WinRTComponent::testTask()
    {
        task<int> t([]() {
            return 42;
        });
    
        // wait and return the value
        return t.get();
    }
    ----------------------------------------
    var comp = new WinRTComponentDll.WinRTComponent();
    var n = comp.testTask();

    Tuesday, April 17, 2012 6:00 AM

Answers

  • As all the Microsoft people keep saying, they want you to do things asynchronously because it improves application responsiveness in many situations. If you find yourself in one of those scenarios where doing something asynchronously just doesn't make sense, I wrote these as a work around:

    template <typename TResult>
    TResult PerformSynchronously(Windows::Foundation::IAsyncOperation<TResult>^ asyncOp)
    {
    	return PerformSynchronously(Concurrency::task<TResult>(asyncOp));
    }
    
    template <typename TResult>
    TResult PerformSynchronously(Concurrency::task<TResult> task)
    {
    	Concurrency::event synchronizer;
    	task.then([&](TResult taskResult) {
    		synchronizer.set();
    	}, Concurrency::task_continuation_context::use_arbitrary() );
    	synchronizer.wait();
    	return task.get();
    }


    Only use it if you've got the good sense to know when it's the right thing to do.

    In your case, just do:

    return PerformSynchronously(t);


    • Proposed as answer by brentAtBrainium Tuesday, April 17, 2012 10:09 PM
    • Edited by brentAtBrainium Tuesday, April 17, 2012 10:10 PM
    • Marked as answer by wqter Thursday, April 19, 2012 7:00 AM
    Tuesday, April 17, 2012 10:08 PM
  • The PPL's task infrastructure is designed not to allow a blocking wait for something asynchronous on the UI thread of a modern application.  An attempt to do so will presently result in an invalid_operation exception which states this.

    If you're trying to expose an asynchronous operation across the boundary between C++ and C# as your code snippets might suggest, you can always use create_async on the C++ side to build an asynchronous operation (IAsync*) which can subsequently be await'ed on the C# side.

    -- Bill

    Tuesday, April 17, 2012 4:49 PM

All replies

  • The PPL's task infrastructure is designed not to allow a blocking wait for something asynchronous on the UI thread of a modern application.  An attempt to do so will presently result in an invalid_operation exception which states this.

    If you're trying to expose an asynchronous operation across the boundary between C++ and C# as your code snippets might suggest, you can always use create_async on the C++ side to build an asynchronous operation (IAsync*) which can subsequently be await'ed on the C# side.

    -- Bill

    Tuesday, April 17, 2012 4:49 PM
  • As all the Microsoft people keep saying, they want you to do things asynchronously because it improves application responsiveness in many situations. If you find yourself in one of those scenarios where doing something asynchronously just doesn't make sense, I wrote these as a work around:

    template <typename TResult>
    TResult PerformSynchronously(Windows::Foundation::IAsyncOperation<TResult>^ asyncOp)
    {
    	return PerformSynchronously(Concurrency::task<TResult>(asyncOp));
    }
    
    template <typename TResult>
    TResult PerformSynchronously(Concurrency::task<TResult> task)
    {
    	Concurrency::event synchronizer;
    	task.then([&](TResult taskResult) {
    		synchronizer.set();
    	}, Concurrency::task_continuation_context::use_arbitrary() );
    	synchronizer.wait();
    	return task.get();
    }


    Only use it if you've got the good sense to know when it's the right thing to do.

    In your case, just do:

    return PerformSynchronously(t);


    • Proposed as answer by brentAtBrainium Tuesday, April 17, 2012 10:09 PM
    • Edited by brentAtBrainium Tuesday, April 17, 2012 10:10 PM
    • Marked as answer by wqter Thursday, April 19, 2012 7:00 AM
    Tuesday, April 17, 2012 10:08 PM
  • Thx Messmer, according to your suggestion, I found the associated topic:
    http://msdn.microsoft.com/EN-US/library/hh750082(VS.110).aspx#example_component

    Below are my code exposing an async op to JS, this operation retrieves current location via Geolocation API (although we can directly use it in JS, this is just a demo):

    IAsyncOperation<Windows::Devices::Geolocation::Geoposition^>^ WinRTComponent::GetCurrentLocationAsync()
    {
    	return m_geo->GetGeopositionAsync();
    }

    In JS:

    var comp = new GeolocationWinRTComp.WinRTComponent();
    
    function GetCurrentLocation()
    {
        try{
            comp.GetCurrentLocationAsync()
                .done(getPositionHandler, getPositionErrorHandler);
        } catch (e) {
            // ...
        }
    }
    
    function getPositionHandler(pos) {
        if (pos) {
            id('Latitude').innerHTML = pos.coordinate.latitude;
            id('Longitude').innerHTML = pos.coordinate.longitude;
            id('Accuracy').innerHTML = pos.coordinate.accuracy;
        } else {
            id('Info').innerHTML = "Failed to get position.";
        }
    }
    
    function getPositionErrorHandler(err) {
        id('Info').innerHTML = "Failed to get position.";
    }
    

    Thursday, April 19, 2012 3:27 AM
  • Thanks a lot Brent, a nice way to synchronize async operations! I tested with my app and it works great :)
    • Marked as answer by wqter Thursday, April 19, 2012 6:57 AM
    • Unmarked as answer by wqter Thursday, April 19, 2012 7:00 AM
    Thursday, April 19, 2012 6:56 AM