boost :: asio :: async_read endet, ohne die Abschlussbedingung zu erfüllen

Auf Windows beobachte ich, dass, wenn einasync_readie @ -Operation wird an einer seriellen Schnittstelle erfolgreich abgeschlossen, und ich initiiere sofort eine andere @ -Operatioasync_read Operation zu lesenn Bytes, das zweiteasync_readie @ -Operation wird sofort mit Erfolg und 0 übertragenen Bytes unerwartet abgeschlossen.

nach dem zweitenasync_read Operation, wenn ein dritterasync_read Operation wird initiiert, um @ zu lesn bytes, dann wird es mit Erfolg abgeschlossen undn übertragene Bytes

// where buffer_size(buffer) and n are both greater than 1
async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) {
  // completes with error=success and bytes_transferred=n
  async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) {
    // complete with error=success and bytes_transferred=0
    async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) {
      // completes with error=success and bytes_transferred=n
    });
  });
});

wenn ein 1-Millisekunden-Schlaf zwischen dem ersten und dem zweiten @ durchgeführt wiasync_read Operation, dann wird die zweite Operation mit Erfolg abgeschlossen undn übertragene Bytes

// where buffer_size(buffer) and n are both greater than 1
async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) {
  // completes with error=success and bytes_transferred=n
  sleep_for(milliseconds(1));
  async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) {
    // completes with error=success and bytes_transferred=n
  });
});

Warum passiert das und wie kann ich das vermeiden?

Für Details verwende ich Boost.Asio unter Windows, um mit einem Mikrocontroller über RS232 zu kommunizieren, der von einem ATXMEGA-192A3U emuliert wird. Ich sende einen Startbefehl an die Steuerung und lese die Ausgabe mit einem Timeout. Ich lese die Ausgabe durch Aufrufen desReadPort -Funktion, für die der Code unten angegeben ist. Das Programm führt die folgenden Leseaufgaben hintereinander aus:

Überprüfen Sie die Antwort des Mikrocontrollers auf den Startbefehl. Mit diesem Lesevorgang können die 3 erwarteten Zeichen erfolgreich gelesen werden:R\r\nLesenn Bytes der Ausgabe von der Steuerung für einige Hundert ms.

Dasasync_readie @ -Operation in Schritt 2 wird unerwartet erfolgreich abgeschlossen, obwohl die Anzahl der angeforderten Bytes nicht gelesen wurde.

class BoostBasedCommunication
{
public:
    BoostBasedCommunication();
    ~BoostBasedCommunication(void);
    /*...*/
    virtual int ReadPort(
        int const numberOfCharacters, // maximum number of characters to be read
        unsigned long const globalTimeout, // maximum time the operation is allowed to take in ms
        unsigned long const intermediateTimeout, // maximum time allowed between two consequtive characters in ms
        int& numberOfCharactersRead
        );
    /*...*/

private:
    /*...*/
    std::vector<unsigned char> inputBuffer; ///< buffer to save data to that is received
    size_t numberOfBytesRead; ///< Number of bytes read
    int lastErrorCode; ///< last error code
    io_service my_io_service; ///< boost io service class
    serial_port port; ///< boost serial port class
    /*...*/
};

// Reads from the port until numberOfCharacters have been read, or the 
// deadline_timer has expired, or the time between two consecutive calls of 
// the completion condition is larger than intermediateTimeoutMS

int BoostBasedCommunication::ReadPort(
    int const numberOfCharacters, // maximum number of characters to be read
    unsigned long const globalTimeoutMS, // maximum time the operation is allowed to take in ms
    unsigned long const intermediateTimeoutMS, // maximum time allowed between two consecutive characters in ms
    int& numberOfCharactersRead // Actual number of characters read
    )
{
    try
    {
        OutputDebugStringA("ReadPort called\r\n");
        my_io_service.reset();
        deadline_timer gloabalTimeout(my_io_service);
        inputBuffer.resize(numberOfCharacters);
        timeoutHandler myGlobalTimeoutHandler(&port);

        completion_handler_2 myHandler(&gloabalTimeout, numberOfBytesRead);
        completion_condition_2 my_completion_condition(intermediateTimeoutMS, numberOfCharacters);

        // Set the timer
        gloabalTimeout.expires_from_now(boost::posix_time::milliseconds(globalTimeoutMS));
        gloabalTimeout.async_wait(myGlobalTimeoutHandler);

        async_read(port, boost::asio::buffer(inputBuffer, numberOfCharacters), my_completion_condition, myHandler);

        my_io_service.run(); // run the io service
        numberOfCharactersRead = numberOfBytesRead;
    }
    catch (std::exception&)
    {
        return COMMUNICATIONFAILED;
    }
    return NOERROR;
}

class completion_condition_2
{
public:
    completion_condition_2(
        long intermediateTimeOutTime,
        size_t numberOfCharactersTobeRead
        ) :intermediateTimeOutTime(intermediateTimeOutTime),
        numberOfCharactersTobeRead(numberOfCharactersTobeRead)
    {}

    std::size_t operator()(
        const boost::system::error_code& error, // Result of latest async_read_some operation.
        std::size_t bytes_transferred // Number of bytes transferred so far.
        )
    {
        if (error)
        {
            OutputDebugStringA(("completion_condition received error code: " + error.message() + "\r\n").c_str());

            if (error.value() == ERROR_OPERATION_ABORTED)
            {
                return 0;
            }
        }

        /* ...Code concerning the intermediate timeout, which is commented out...*/

        if (numberOfCharactersTobeRead <= bytes_transferred) // Enough data has been read
        {
            std::stringstream message;
            message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => done!" << std::endl;
            OutputDebugStringA(message.str().c_str());
            return 0;
        }
        else // More data should be read.
        {
            std::stringstream message;
            message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => continue!" << std::endl;
            OutputDebugStringA(message.str().c_str());
            return numberOfCharactersTobeRead - bytes_transferred;
        }
    }

private:
    size_t numberOfCharactersTobeRead; ///< Number of characters to be read
};

class completion_handler_2 {
public:
    completion_handler_2(
        deadline_timer* _globalTimeout,
        size_t& numberOfBytesRead
        ) :_globalTimeout(_globalTimeout),
        numberOfBytesRead(numberOfBytesRead)
    {
    }

    void operator()(
        const boost::system::error_code& error, // Result of operation.
        std::size_t bytes_transferred           // Number of bytes read.
        )
    {
        OutputDebugStringA(("completion handler called with error code: " + error.message() + "\r\n").c_str());
        if (error)
        {
            if (error.value() == ERROR_OPERATION_ABORTED)
            {
                numberOfBytesRead = bytes_transferred;
                return;
            }
            else
            {
                BOOST_THROW_EXCEPTION(std::exception("Communication failed"));
            }
        }

        OutputDebugStringA("completion handler: timeout cancelation.\r\n");

        _globalTimeout->cancel();
        numberOfBytesRead = bytes_transferred;
    }

private:
    deadline_timer* _globalTimeout; ///< global timeout deadline timer
    size_t& numberOfBytesRead; ///< number of bytes read
};

Wenn ich den ersten Lesevorgang durchführe, der wie erwartet funktioniert, erhalte ich die folgende Ausgabe:

ReadPort called
completion_condition: bytes transferred: 0 of 3 => continue!
completion_condition: bytes transferred: 3 of 3 => done!
completion handler called with error code: success
completion handler timeout cancelation.
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request

Wenn ich sofort nach Abschluss des ersten Lesevorgangs einen weiteren Lesevorgang durchführe, wird der Vorgang nach 2 ms mit der folgenden Ausgabe abgeschlossen:

ReadPort called
completion_condition: bytes transferred: 0 of 1024 => continue!
completion handler called with error code: success // Why is the completion handler called here, although the completion condition did not return 0?
completion handler timeout cancelation.
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request

Ein dritter Lesevorgang, unmittelbar nach dem letzten, funktioniert wie erwartet:

ReadPort called
completion_condition: bytes transferred: 0 of 1024 => continue!
completion_condition: bytes transferred: 8 of 1024 => continue!
...
completion_condition: bytes transferred: 88 of 1024 => continue!
completion_condition: bytes transferred: 96 of 1024 => continue!
timeoutHandler called cancel of seriel port.
completion_condition received error code: The I/O operation has been aborted because of either a thread exit or an application request
completion handler called with error code: The I/O operation has been aborted because of either a thread exit or an application request

Antworten auf die Frage(2)

Ihre Antwort auf die Frage