locked
Task wait until condition is met

    Question

  • I have a user defined aync function in which I am calling another async function. The reply of this second async call is available in a callback. Its like below:

    IAsyncAction^ MyClass::GetVal()

    {

      // Intiate second async call.

      DataWriter^ Writer;

     Writer->StoreAsync(.. ); // as part of sending a request via socket

    }

    void MyClass::CallBack()

    {

      // Get the reply of the StoreAsync in this function and set a flag as true.

    }

    When calling GetVal() I need GetVal() to wait until the call back is received and the flag is set to true so that I can get value of the flag once GetVal() finishes. How can I make GetVal() wait until the callback is received?

    Right now I am calling GetVal() as below:

    IAsyncAction^ act = GetVal();

    task<void>tsk = create_task( act );

    tsk.then([]()

    {

     // read the value of the flag here.

    }.wait();

    But currently the  GetVal() task finishes and the then[] lamda is executed before the callback is received. How can this be solved?

    Wednesday, October 03, 2012 1:31 AM

Answers

  • What is the asynchronous action and what is the value that MyClass::GetVal is returning?  If the expectation is that the semantic meaning is not completed until the socket request is done, then don't complete the asynchronous construct until that point.  Without seeing what GetVal is returning, it's difficult to give you an exact example, but it's certainly possible to do something like:

    //
    // GetVal suggests a value is being returned -- I'll just substitute T for this in the below.
    //
    task<T> MyClass::GetVal()
    {
        // ...
        DataWriter^ writer = ...;
        // ...
        return create_task(writer->StoreAsync(...)).then([=](unsigned int){
            // ...
            T value = ...;
            return T;
        });
    }

    If you require this to be an IAsync* such that it can function across the ABI boundary as part of a WinRT component, the same can be done wrapped in a create_async call:

    //
    // GetVal suggests a value is being returned -- I'll just substitute T for this in the below.
    //
    IAsyncOperation<T>^ MyClass::GetVal()
    {
        return create_async([=](){
            // ...
            DataWriter^ writer = ...;
            // ...
            return create_task(writer->StoreAsync(...)).then([=](unsigned int){
                // ...
                T value = ...;
                return T;
            });
        });
    }

    With a form like this, you can consume the GetVal call much as you have:

    //
    // The task<T> returning form:
    //
    GetVal().then([=](T value){
        // The value is valid here.
    });
    
    //
    // The IAsyncOperation<T> returning form:
    //
    create_task(GetVal()).then([=](T value){
        // The value is valid here
    });

    It's much better to work within the framework where possible and keep asynchronous things flowing asynchronously rather than trying to stitch things together with synchronous waits.

    • Marked as answer by Jesse Jiang Wednesday, October 10, 2012 6:20 AM
    Wednesday, October 03, 2012 5:35 PM

All replies

  • You can try by followings.....

    HANDLE CallBackReceivedEvent=CreateEventEx( NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS );
    
    IAsyncAction^ MyClass::GetVal()
    
    {
    ResetEvent(CallBackReceivedEvent); 
    
      // Intiate second async call.
      DataWriter^ Writer;
     Writer->StoreAsync(.. ); // as part of sending a request via socket
    
    WaitForSingleObjectEx(CallBackReceivedEvent, INFINITE, TRUE);   // Wait until CallBackReceivedEvent is not set
    }
    
    void MyClass::CallBack()
    
    {
      // Get the reply of the StoreAsync in this function and set a flag as true.
    SetEvent(CallBackReceivedEvent);
    }

    In above case you should run this sync procedure in a separate worker thread in WinRT, bccause UI thread does not expect any blocking.

    If you would like to do this under UI thread u can follow this approach..

    volatile boolean flag;
    
    IAsyncAction^ MyClass::GetVal()
    
    {
    flag = false; 
    
      // Intiate second async call.
      DataWriter^ Writer;
     Writer->StoreAsync(.. ); // as part of sending a request via socket
    
    while( true != flag ) 
    		CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneIfPresent);
    }
    
    void MyClass::CallBack()
    
    {
      // Get the reply of the StoreAsync in this function and set a flag as true.
    flag = true; // Set after end of Callback
    }


    Wednesday, October 03, 2012 2:44 AM
  • What is the asynchronous action and what is the value that MyClass::GetVal is returning?  If the expectation is that the semantic meaning is not completed until the socket request is done, then don't complete the asynchronous construct until that point.  Without seeing what GetVal is returning, it's difficult to give you an exact example, but it's certainly possible to do something like:

    //
    // GetVal suggests a value is being returned -- I'll just substitute T for this in the below.
    //
    task<T> MyClass::GetVal()
    {
        // ...
        DataWriter^ writer = ...;
        // ...
        return create_task(writer->StoreAsync(...)).then([=](unsigned int){
            // ...
            T value = ...;
            return T;
        });
    }

    If you require this to be an IAsync* such that it can function across the ABI boundary as part of a WinRT component, the same can be done wrapped in a create_async call:

    //
    // GetVal suggests a value is being returned -- I'll just substitute T for this in the below.
    //
    IAsyncOperation<T>^ MyClass::GetVal()
    {
        return create_async([=](){
            // ...
            DataWriter^ writer = ...;
            // ...
            return create_task(writer->StoreAsync(...)).then([=](unsigned int){
                // ...
                T value = ...;
                return T;
            });
        });
    }

    With a form like this, you can consume the GetVal call much as you have:

    //
    // The task<T> returning form:
    //
    GetVal().then([=](T value){
        // The value is valid here.
    });
    
    //
    // The IAsyncOperation<T> returning form:
    //
    create_task(GetVal()).then([=](T value){
        // The value is valid here
    });

    It's much better to work within the framework where possible and keep asynchronous things flowing asynchronously rather than trying to stitch things together with synchronous waits.

    • Marked as answer by Jesse Jiang Wednesday, October 10, 2012 6:20 AM
    Wednesday, October 03, 2012 5:35 PM