A
Antonio Di Monaco
Hi,
I don't know if I'm [OT] about my question here, but I didn't find any
Boost-related groups.
I have a question about Boost Asio, better say a need of clarification.
Let me summarize my problem
first. I need to control a TCP device, that works like that:
a) it always wait for a command, and returns a response
b) both command and response lengths are known in advance, so I know how
many bytes I expect in both directions
c) sequence command/response is atomic, i.e. it doesn't accept another
command before having replied to the first one
So I implemented synchronous read/write operations using Boost Asio. I
clarify that my solution works perfectly, I just need
to know if it was implemented in the way it should be, cause it was a
result of "try-and-pray", and maybe my result isn't the
proper way to manage this problem.
For the sake of brevity, I'm posting only write method, cause read one
has the same layout:
int IONetworkSocket::doWrite(const std::vector< unsigned char > &vData)
{
boost::system::error_code timerResult;
boost::system::error_code writeResult;
std::size_t bytesWritten = 0;
bool timeout = false, ioError = false, ignoreTimeout = false,
ignoreIoError = false;
unsigned int transaction = _currentTransaction;
boost::asio::deadline_timer timer(ioService());
timer.expires_from_now(boost:osix_time::milliseconds(writeTimeOut()));
timer.async_wait([&timerResult, &timeout, &ignoreIoError, transaction,
this] (const boost::system::error_code &cError)
{
boost::lock_guard< boost::mutex >
guard(this->_transactionMutex);
if (transaction == this->_currentTransaction)
{
++(this->_currentTransaction);
timeout = true;
timerResult = cError;
ignoreIoError = true;
}
});
boost::asio::async_write(socket(),boost::asio::buffer(vData,vData.size()),boost::asio::transfer_all(),
[&writeResult, &ioError, &bytesWritten,
&ignoreTimeout, &timer, transaction, this]
(const boost::system::error_code &cError,
std::size_t iBytesTransferred)
{
boost::lock_guard< boost::mutex >
guard(this->_transactionMutex);
if (transaction == this->_currentTransaction)
{
++(this->_currentTransaction);
writeResult = cError;
bytesWritten = iBytesTransferred;
ioError = cError.value() != 0;
ignoreTimeout = true;
timer.cancel();
}
});
ioService().reset();
boost::system::error_code ioEc;
while (ioService().run_one(ioEc))
if (ioError && !ignoreIoError)
{
... something went wrong during I/O, return with error code ...
}
else if (timeout && !ignoreTimeout)
{
... timeout occurred, return with timeout error code ...
}
... here everything should be fine ...
}
The usage of "while (ioService().run_one(ioEc))" was the result of
googling around, and that's my doubt.
What I was trying to implement is:
a) block my thread, waiting for only ONE of the two handlers to complete
b) when one of the two handlers completes, I'd like to destroy the other
handler, i.e. I don't mind if it completes or not,
but I don't want to be notified of it
My first version called ioService().run_one(ioEc) without any while
loop, and had no mutex-protected _currentTransaction member.
What happened is that, even if I call ioService().reset(), when
performing read after write, all past handlers are processed,
accessing to references of variables that no longer exist.
I solved my problem using a transaction number, that is "frozen" in both
handlers at invocation, due to pass-by-value during
capture. When one of the handlers get called, increments the transaction
number, so if in future past handlers are called, they
go out quickly without any damages.
Are there any better solutions? This is my first attempt with Boost
Asio, probably I misunderstood some of its basic principles
Thanks a lot,
Antonio.
I don't know if I'm [OT] about my question here, but I didn't find any
Boost-related groups.
I have a question about Boost Asio, better say a need of clarification.
Let me summarize my problem
first. I need to control a TCP device, that works like that:
a) it always wait for a command, and returns a response
b) both command and response lengths are known in advance, so I know how
many bytes I expect in both directions
c) sequence command/response is atomic, i.e. it doesn't accept another
command before having replied to the first one
So I implemented synchronous read/write operations using Boost Asio. I
clarify that my solution works perfectly, I just need
to know if it was implemented in the way it should be, cause it was a
result of "try-and-pray", and maybe my result isn't the
proper way to manage this problem.
For the sake of brevity, I'm posting only write method, cause read one
has the same layout:
int IONetworkSocket::doWrite(const std::vector< unsigned char > &vData)
{
boost::system::error_code timerResult;
boost::system::error_code writeResult;
std::size_t bytesWritten = 0;
bool timeout = false, ioError = false, ignoreTimeout = false,
ignoreIoError = false;
unsigned int transaction = _currentTransaction;
boost::asio::deadline_timer timer(ioService());
timer.expires_from_now(boost:osix_time::milliseconds(writeTimeOut()));
timer.async_wait([&timerResult, &timeout, &ignoreIoError, transaction,
this] (const boost::system::error_code &cError)
{
boost::lock_guard< boost::mutex >
guard(this->_transactionMutex);
if (transaction == this->_currentTransaction)
{
++(this->_currentTransaction);
timeout = true;
timerResult = cError;
ignoreIoError = true;
}
});
boost::asio::async_write(socket(),boost::asio::buffer(vData,vData.size()),boost::asio::transfer_all(),
[&writeResult, &ioError, &bytesWritten,
&ignoreTimeout, &timer, transaction, this]
(const boost::system::error_code &cError,
std::size_t iBytesTransferred)
{
boost::lock_guard< boost::mutex >
guard(this->_transactionMutex);
if (transaction == this->_currentTransaction)
{
++(this->_currentTransaction);
writeResult = cError;
bytesWritten = iBytesTransferred;
ioError = cError.value() != 0;
ignoreTimeout = true;
timer.cancel();
}
});
ioService().reset();
boost::system::error_code ioEc;
while (ioService().run_one(ioEc))
if (ioError && !ignoreIoError)
{
... something went wrong during I/O, return with error code ...
}
else if (timeout && !ignoreTimeout)
{
... timeout occurred, return with timeout error code ...
}
... here everything should be fine ...
}
The usage of "while (ioService().run_one(ioEc))" was the result of
googling around, and that's my doubt.
What I was trying to implement is:
a) block my thread, waiting for only ONE of the two handlers to complete
b) when one of the two handlers completes, I'd like to destroy the other
handler, i.e. I don't mind if it completes or not,
but I don't want to be notified of it
My first version called ioService().run_one(ioEc) without any while
loop, and had no mutex-protected _currentTransaction member.
What happened is that, even if I call ioService().reset(), when
performing read after write, all past handlers are processed,
accessing to references of variables that no longer exist.
I solved my problem using a transaction number, that is "frozen" in both
handlers at invocation, due to pass-by-value during
capture. When one of the handlers get called, increments the transaction
number, so if in future past handlers are called, they
go out quickly without any damages.
Are there any better solutions? This is my first attempt with Boost
Asio, probably I misunderstood some of its basic principles
Thanks a lot,
Antonio.