turkycat
turkycat

Reputation: 11

.wait() on a task in c++/cx throws exception

I have a function which calls Concurrency::create_task to perform some work in the background. Inside that task, there is a need to call a connectAsync method on the StreamSocket class in order to connect a socket to a device. Once the device is connected, I need to grab some references to things inside the connected socket (like input and output streams).

Since it is an asynchronous method and will return an IAsyncAction, I need to create another task on the connectAsync function that I can wait on. This works without waiting, but complications arise when I try to wait() on this inner task in order to error check.

Concurrency::create_task( Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService::FromIdAsync( device_->Id ) )
    .then( [ this ]( Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService ^device_service_ )
{
    _device_service = device_service_;
    _stream_socket = ref new Windows::Networking::Sockets::StreamSocket();

    // Connect the socket
    auto inner_task = Concurrency::create_task( _stream_socket->ConnectAsync(
        _device_service->ConnectionHostName,
        _device_service->ConnectionServiceName,
        Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication ) )

        .then( [ this ]()
    {

       //grab references to streams, other things.

    } ).wait(); //throws exception here, but task executes

Basically, I have figured out that the same thread (presumably the UI) that creates the initial task to connect, also executes that task AND the inner task. Whenever I attempt to call .wait() on the inner task from the outer one, I immediately get an exception. However, the inner task will then finish and connect successfully to the device.

Why are my async chains executing on the UI thread? How can i properly wait on these tasks?

Upvotes: 1

Views: 2591

Answers (2)

Elliot
Elliot

Reputation: 2033

You could set an event and have the main thread wait for it. I have done this with some IO async operations. Here is a basic example of using the thread pool, using an event to wait on the work:

    TEST_METHOD(ThreadpoolEventTestCppCx)
    {

        Microsoft::WRL::Wrappers::Event m_logFileCreatedEvent;
        m_logFileCreatedEvent.Attach(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));

        long x = 10000000;

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

            while (x--);

            SetEvent(m_logFileCreatedEvent.Get());

        });

        auto asyncAction = ThreadPool::RunAsync(workItem);

        WaitForSingleObjectEx(m_logFileCreatedEvent.Get(), INFINITE, FALSE);


        long i = x;
    }

Here is a similar example except it includes a bit of Windows Runtime async IO:

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;

}

I tried using an event to wait on Windows Runtime Async work, but it blocked. That's why I had to use the threadpool.

Upvotes: 1

Peter Torr
Peter Torr

Reputation: 12019

In general you should avoid .wait() and just continue the asynchronous chain. If you need to block for some reason, the only fool-proof mechanism would be to explicitly run your code from a background thread (eg, the WinRT thread pool).

You could try using the .then() overload that takes a task_options and pass concurrency::task_options(concurrency::task_continuation_context::use_arbitrary()), but that doesn't guarantee the continuation will run on another thread; it just says that it's OK if it does so -- see documentation here.

Upvotes: 3

Related Questions