boost :: asio :: async_read endet, ohne die Abschlussbedingung zu erfüllen
Auf Windows beobachte ich, dass, wenn einasync_read
ie @ -Operation wird an einer seriellen Schnittstelle erfolgreich abgeschlossen, und ich initiiere sofort eine andere @ -Operatioasync_read
Operation zu lesenn
Bytes, das zweiteasync_read
ie @ -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:
R\r\n
Lesenn
Bytes der Ausgabe von der Steuerung für einige Hundert ms.Dasasync_read
ie @ -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