locked
Concurrency::invalid_operation when calling task<T>::get()

    Question

  • Code like this:

    task<void>(m_s->BindEndpointAsync(host, service)).get();

    throws:

    Concurrency::invalid_operation("Illegal to wait on a task in a WinRT STA")

    So, I can't wait on the task like this, it's a pity, so how should I do it ? What's the easiest way ?


    Thursday, March 1, 2012 4:50 PM

Answers

  • Excessive waiting on the UI thread may be detected when trying to publish your app to the store and cause your app to be rejected.

    From the PPL side of things you shoud be using a continuation to call the task<>.get() as shown in the sample code.

    I.e., from this:

    	try
    	{
    		task<void>( []() { for(int i=1; i>0; i++);  } ).get();
    	}
    	catch(std::exception& e)
    	{
    		::OutputDebugStringA(e.what());
    	}

    To this:

    	try
    	{
    		task<int>( []() { for(int i=1; i>0; i++); return 1;  } ).then(
    			[](task<int> previousTask)
    		{
    			int ret;
    			ret = previousTask.get();
    		});
    	}
    	catch(std::exception& e)
    	{
    		::OutputDebugStringA(e.what());
    	}
    The first throws, the second does not.
    Friday, March 2, 2012 11:52 PM
    Moderator

All replies

  • Have you had a chance to review the C++ version of the StreamSocket Sample which demonstrates the use of tasks?

    Avoiding blocking on the UI thread is key to building a responsive Metro style app.
     See the section on Asynchronous operation in the Windows Runtime design document.

    The ppltasks.h notes the reason this exception would be raised:

                // In order to prevent WinRT STA threads from blocking the UI, calling task.wait() is illegal for STA
                // threads, and task.get() is illegal if it has to wait.
                if (_IsSTA() && (_FromWait || _M_Completed.wait(0) == COOPERATIVE_WAIT_TIMEOUT))
                {
                    throw invalid_operation("Illegal to wait on a task in a WinRT STA");
                }

    Thanks,

    -David

    .

    Friday, March 2, 2012 3:29 AM
    Moderator
  • Yes, I saw this sample, I understand this "good design" thing, but I NEED to do what I want to do, after all I'm the developer of my application and it's up to me to decide what path to take. I have some old code that needs to run in metro environment and there's no way I can rewrite it from scratch. In the developer preview I could write like this:

                m_s->Information->LocalHostName = host;
                m_s->Information->LocalServiceName = service;
    
                auto operation = m_s->BindServiceNameAsync();
    
                WRL::Wrappers::Event evt(CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS));
                HRESULT hr = 0;
                
                operation->Completed = ref new AsyncActionCompletedHandler(
                    [&evt, &hr](IAsyncAction^ asyncInfo)
                    {
                       hr = asyncInfo->ErrorCode.Value;
                       SetEvent(evt.Get());                    
                    }, Platform::CallbackContext::Any);
    
                operation->Start();
    
                WaitForSingleObjectEx(evt.Get(), INFINITE, FALSE);

    But now it's not possible, so, is there any other way I can do the same in Windows 8 beta without some ugly tricks ? If not, then I'll have to invent those ugly tricks anyway and many other developers that are porting existing code would have to do the same...

    Friday, March 2, 2012 5:53 AM
  • Excessive waiting on the UI thread may be detected when trying to publish your app to the store and cause your app to be rejected.

    From the PPL side of things you shoud be using a continuation to call the task<>.get() as shown in the sample code.

    I.e., from this:

    	try
    	{
    		task<void>( []() { for(int i=1; i>0; i++);  } ).get();
    	}
    	catch(std::exception& e)
    	{
    		::OutputDebugStringA(e.what());
    	}

    To this:

    	try
    	{
    		task<int>( []() { for(int i=1; i>0; i++); return 1;  } ).then(
    			[](task<int> previousTask)
    		{
    			int ret;
    			ret = previousTask.get();
    		});
    	}
    	catch(std::exception& e)
    	{
    		::OutputDebugStringA(e.what());
    	}
    The first throws, the second does not.
    Friday, March 2, 2012 11:52 PM
    Moderator
  • Edit: I've figured out a way to make an IAsyncOperation synchronous. See the code below.

    Completely agree. I'm porting some iOS games to Metro, and I can't even get a path to a resource file without it being asynchronous?!? This is ridiculous! >:( The UI remaining "responsive" is meaningless if I'm still trying to load textures for the interface in the first place! Let *ME* decide whether an operation should be synchronous or not. 

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


    Thursday, April 12, 2012 11:58 PM
  • hi brent,

    i used your PerformSynchronously function, but there is a wierd problem.

    sometimes, the app doesn't run into the synchronizer.set();

    and synchronizer.wait(); wil just wait there...

    do you know the possible cause?

    Tuesday, May 8, 2012 7:43 AM
  • David (and Steve).

    I understand why i cannot use wait() or get() from within UI thread, but why i cannot use it from non-UI thread ? It doesn't make sense. Microsoft is going from one extreme to another. Simply when i'm not in UI thread i need a way how to wait until any async operation finish and then continue (with results from that async operation).

    Friday, May 18, 2012 7:40 AM
  • This is another synchronization workaround:

    volatile int status = 0;
    
    task<void> connect(socket->ConnectAsync(...));
    connect.then([this,&status] (task<void> previous) {
    	try {
    		previous.get();
    		OutputDebugStringW(L"Connected\n");
    		status = 1;
    	} catch (...) {
    		OutputDebugStringW(L"Failed\n");
    		status = -1;
    	}
    });
    
    while(status == 0) {
    	CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(ProcessOneIfPresent);
    	OutputDebugStringW(L"Waiting\n");
    }
    
    OutputDebugStringW(L"Done\n");

    The result is:
    Waiting
    Waiting
    Waiting
    Waiting
    Connected
    Waiting
    Done

    • Edited by frustum Thursday, May 24, 2012 3:13 PM
    Thursday, May 24, 2012 3:09 PM