Boost logo

Boost Users :

Subject: [Boost-users] Timing out when writing to serial port while still receiving data
From: Goetz, Russ (Russ_Goetz_at_[hidden])
Date: 2011-02-21 09:02:05


I've created a wrapper class around boost asio's serial_port class:

class SerialPort
{
  public:
        void open();
        void send(const std::vector<uint8> &data);
        void close();
  private:
        void _startReceiving();
        boost::asio::serial_port m_serialPort;
        boost::asio::io_service m_ioService;
        std::vector<uint8> m_recvBuf;
};

The SerialPort class has an 'open' member function that opens the port
and begins waiting for data to be received on the serial port. There's
also a 'send' member function that writes the specified data to the
serial port. Below is the code (running on a separate thread) that waits
for data to be received on the serial port:

Receive Thread
--------------
while (m_recvData)
{
        m_serialPort.async_read_some(boost::asio::buffer(m_recvBuf),
              boost::bind(&SerialPort::_recvHandler, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));

        m_ioService.run();
        m_ioService.reset();
}

The problem I'm experiencing is that when the flow control of the serial
port is set to RTS/CTS, and I try and write to the serial port with the
cable unplugged or the CTS serial line otherwise low, the call is a
blocking call. I realize this is the way it's supposed to work, but I
need to make writing to the serial port timeout after a few hundred
milliseconds instead of blocking until CTS goes high.

So I found code on the boost user's mailing list to do just that
(m_writeDoneEvent is a manual reset event):

Write Thread
------------
boost::optional<boost::system::error_code> timerResult;
boost::asio::deadline_timer writeTimer(m_ioService);
writeTimer.expires_from_now(milliseconds(100));
writeTimer.async_wait(boost::bind(&SerialPort::_writeDone, this,
&timerResult, (std::size_t*)NULLPTR, _1, 0));

boost::optional<boost::system::error_code> writeResult;
std::size_t bytesWritten;
boost::asio::async_write(m_serialPort, boost::asio::buffer(data,
length),
        boost::bind(&SerialPort::_writeDone, this, &writeResult,
&bytesWritten, _1, _2));

// Wait for the write to finish, either due to success or timing out.
m_writeDoneEvent.wait();
m_writeDoneEvent.unset();
if (writeResult)
{
    // Write completed successfully.
    writeTimer.cancel();
    if (writeResult->value())
    {
        std::ostringstream oss;
        oss << "Failed to send some or all of the data to " <<
m_portName
            << ". Successfully sent " << bytesWritten << " of a possible
"
            << length << " bytes.";
        throw SerialException(oss.str(), writeResult->value());
    }
}
else if (timerResult)
{
    // Write timed out. Cancel all asynchronous operations (including
the
    // asynchronous call to read).
    m_serialPort.cancel(); // <==== ***Throws exception***
    std::ostringstream oss;
    oss << "Sending to " << m_portName << " timed out.";
    throw SerialException(oss.str());
}

void SerialPort::_writeDone(boost::optional<boost::system::error_code>
*errorToSet, std::size_t *bytesWrittenToSet,
                const boost::system::error_code &error, std::size_t
bytesWritten) const
{
    if (error != boost::asio::error::operation_aborted)
    {
        // Set the error code so the caller knows why the asynchronous
write
        //operation finished.
        errorToSet->reset(error);
        if (bytesWrittenToSet != NULLPTR)
        {
            *bytesWrittenToSet = bytesWritten;
        }
        // Signal to the caller that the write operation is complete.
        m_writeDoneEvent.set();
    }
}

However, when the write operation on the serial port times out, the call
to m_serialPort.cancel() above throws an exception, saying 'The
attempted operation is not supported for the type of object referenced'.
Looking at the source code, the comment where the exception is thrown
says that it's an unsafe operation to call cancel on the IO service from
a thread when work has been given to the IO service from a different
thread. This obviously fits my case - I've submitted an async_read on
the Receive thread and I'm trying to cancel all asynchronous work on the
Write thread.

Is it possible to have a thread always running that's waiting for data
to be received on the serial port as well as have a write thread that
timeouts? If so, where did I go wrong above?

Thanks,
Russ


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net