locked
Strange problem with asynchronous tasks

    Question

  • This is the simple function.

    task<void> ExtCommandReader::ReadHeader() { auto readBuf = ref new Buffer(static_cast<unsigned int>(3)); return task<IBuffer^>(m_conn->m_socket->InputStream->ReadAsync(readBuf, 3, InputStreamOptions::None)).then([=](task<IBuffer^> previousTask) { try { IBuffer^ ib = previousTask.get(); DataReader ^dr = DataReader::FromBuffer(ib); dr->UnicodeEncoding = UnicodeEncoding::Utf8; dr->ByteOrder = ByteOrder::BigEndian; unsigned int n = ib->Length; if (n < 3) throw ref new FailureException("failed to read ext command header"); requestCode = dr->ReadByte(); dataSize = dr->ReadUInt16(); } catch (Exception ^e) { getApp()->DebugPrint("ExtCommandReader::ReadHeader: Exception: " + e->Message); throw; } }); }

    Now I have the dump file where the Access Violation exception is stored. The analysis shoed that the access violation occurred when invoking AsyncOperation::GetResults (within the then function above), and the AsyncOperation is NULL. All that lies within concurrency::tasks machinery and is pretty hard to read.

    The AsyncOperation instance (that is NULL) is the member of task class. So, I am confused: why can it become zero?

    Thank you

    Iakov

    Tuesday, November 05, 2013 12:48 PM

All replies

  • Why are you returning a task and then trying to work with the task's completion in the same function? What exactly does the caller of the function ExtCommandReader::ReadHeader() do? Does it try to extract data from the completion as well?

    What happens when you do this and not return the task to the caller?

    void ExtCommandReader::ReadHeader()
    {
        task<IBuffer^>(m_conn->m_socket->InputStream->ReadAsync(readBuf, 3, InputStreamOptions::None)).then([=](task<IBuffer^> previousTask)
        { 
            ... remainder of the code.


    Windows Store Developer Solutions, follow us on Twitter: @WSDevSol|| Want more solutions? See our blog

    Tuesday, November 05, 2013 11:58 PM
    Moderator
  • Why are you returning a task and then trying to work with the task's completion in the same function?

    Strange question. I believe that the construct task.then(...) is also a task and can be returned as a function value.

    Also I believe that the code within then block is executed after the ReadAsync is completed and the data is ready for processing.

    What exactly does the caller of the function ExtCommandReader::ReadHeader() do?

    This is what the caller does:

    task<void> ExtCommandReader::ReadExtensionCommand()
    {
    	return ReadHeader().then([=]()
    	{
    		return m_conn->ReadBytes (dataSize);
    	}).then([=](Array<byte> ^data)
    	{
    		return m_conn->ProcessExtensionCommand (requestCode, data);
    	});
    }
    


    What happens when you do this and not return the task to the caller?

    void ExtCommandReader::ReadHeader()
    {
        task<IBuffer^>(m_conn->m_socket->InputStream->ReadAsync(readBuf, 3, InputStreamOptions::None)).then([=](task<IBuffer^> previousTask)
        { 
            ... remainder of the code.

    If you mean code like this:

    ReadHeader();
    ReadData();
    ProcessCommand();
    

    this code will be incorrect, because ReadData needs DataSize that will be available only AFTER ReadHeader is completed. ProcessCommand needs actual data bytes as well. Without then(...) the ReadData will be executed asynchronously with respect to the ReadHeader, whereas I need it to be executed stricitly after.

    Iakov

    Wednesday, November 06, 2013 6:48 AM
  • I also don't understand why you are exposing ExtCommandReader as a task. If you need to have cascading I/O tasks then the completion for reading the header would be ReadData task and it's completion would be a ProcessCommand task.
    Wednesday, November 06, 2013 9:42 PM
  • I also don't understand why you dictate me how I should write programs.

    Is the program incorrect?

    Is it prohibited to return tasks as function values?

    Is it correct that if the program makes use of tasks, all of the tasks that make chain must be defined in the same function and cannot be passed across function boundaries - as return values, for example?


    • Edited by Яков Wednesday, November 06, 2013 10:19 PM
    Wednesday, November 06, 2013 10:18 PM
  • Your approach is not wrong at all, you can certainly return a task as return values and chain them the way you are doing it. I just wanted to understand what the caller is doing with the returned task.

    Since you are getting an Access Violation exception, can you share your dump file via Skydrive where you get this exception so that we can look?


    Windows Store Developer Solutions, follow us on Twitter: @WSDevSol|| Want more solutions? See our blog

    Thursday, November 07, 2013 12:05 AM
    Moderator
  • Here is the dump: http://sdrv.ms/15P4xtc.

    Iakov

    Friday, November 08, 2013 7:54 AM
  • Can you share a copy of your symbol file too: SpecialistMetro.pdb ?

    Windows Store Developer Solutions, follow us on Twitter: @WSDevSol|| Want more solutions? See our blog

    Friday, November 08, 2013 7:42 PM
    Moderator
  • Here is the symbols file: http://sdrv.ms/15P4xtc

    Iakov

    Friday, November 08, 2013 9:06 PM
  • Here's the callstack leading to the exception:

    0:003> r
    Last set context:
    eax=0226e8c0 ebx=0356dc50 ecx=00000000 edx=0226e8b8 esi=0c959c58 edi=0c959c58
    eip=003f35cd esp=0226e8b0 ebp=0226e8cc iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
    SpecialistMetro!Windows::UI::Popups::IMessageDialog::Commands::get+0x3d:
    003f35cd 8b01            mov     eax,dword ptr [ecx]  ds:0023:00000000=????????
    
    0:003> kp
      *** Stack trace for last set context - .thread/.cxr resets it
    ChildEBP RetAddr  
    0226e8cc 005e1df6 SpecialistMetro!Windows::UI::Popups::IMessageDialog::Commands::get(void)+0x3d
    0226e918 005e1c67 SpecialistMetro!SpecialistMetro::ExtCommandReader::ReadHeader(void)+0x66
    0226e998 00518a6a SpecialistMetro!SpecialistMetro::ExtCommandReader::ReadExtensionCommand(void)+0xc7
    0226e9c8 00515750 SpecialistMetro!SpecialistMetro::ClientConnection::ReadExtensionCommand(void)+0x6a
    0226ea34 005aea8a SpecialistMetro!<lambda_9b6875054dd5ea03fcd0666d21c0841e>::operator()(unsigned char msgType = 0x1e '')+0x120
    ...
    ...
    

    In the callstack above, your ReadHeaderFunction seems to be retrieving the MessageDialog::Commands property that leads to the Access Violation.

    In the code you pasted above, I do not see any MessageDialog in your ReadHeader function, so where and how are you using the MessageDialog?

    The exact source line that calls the MessageDialog::Commands is in extcommandreader.cpp at line 51.


    Windows Store Developer Solutions, follow us on Twitter: @WSDevSol|| Want more solutions? See our blog

    Friday, November 08, 2013 10:02 PM
    Moderator
  • Thank you for your help.

    The ExtCommandReader::ReadHeader function text is posted in the initial message. Line 51 is the closing line of the then "operator":

    task<void> ExtCommandReader::ReadHeader()
    {
    	auto readBuf = ref new Buffer(static_cast<unsigned int>(3));
    	return task<IBuffer^>(...).then([=](task<IBuffer^> previousTask)
    	{
    ...
    	}); <--------- Line 51
    }

    Iakov


    • Edited by Яков Saturday, November 09, 2013 6:40 AM
    Saturday, November 09, 2013 6:37 AM