简体   繁体   中英

Boost::asio: Port cancels itself second time it's read after cancel

I posted a very similar question before, but it hasn't been fully answered yet.

(I'm trying to read a serial port repeatedly but I have to test what happens when the wire is disconnected. Apparently the first time it will time out as expected, but the second time the port seems to cancel it's own operation immediately and will just hang the thread. That is a problem.

I posted the code below. Some lines are LOG lines that come from our logging system, please ignore those, but the comments under those lines will say if the the log fires or not.

I have no clue why it behaves like this. First it just fires the timer and cancels the port and then I try again and it immediately cancels the port with an boost::error_code = 125

boost::asio::io_context io;
boost::asio::serial_port port;

std::size_t RS485CommunicationLayer::readUntil(std::vector<char>& buffer, char delim, std::chrono::microseconds timeout) {
    using boost::system::error_code;
    boost::optional<error_code> read_result;
    boost::optional<error_code> timer_result;
    size_t msglen = 0;
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: start"); 
// runs 1st and 2nd
    io.reset();

    boost::asio::system_timer timer(io, timeout);
    boost::asio::async_read_until(port, boost::asio::dynamic_buffer(buffer), delim, [&](error_code ec, size_t n) {
            if(!timer_result) {
                read_result = ec;
                timer.cancel();
                msglen = n;
                if(ec)
                    LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil() :: Error %s", ec.message().c_str());
// runs 2nd time only
            } // else canceled, do nothing
        }
    );
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: read action set");
    timer.async_wait([&](error_code ec) { 
        if (!read_result) {
            timer_result = ec;
            port.cancel();
            if(ec) {
                LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil() :: Timed out with error %s", ec.message().c_str());    
            } else {
                LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil() :: Timed out");
// runs 1st time only " RS485CommunicationLayer::readUntil() :: Error Operation canceled "
            }
            throw exception::read_exception(exception::read_exception::Code::TIME_OUT);
        } // else canceled, do nothing
    });
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: timer set : %d", static_cast<int>(timeout.count()));
    io.run();
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: ran");
// runs 1st time only
    if (read_result.get()) {
        std::stringstream sstream;
        sstream << read_result.get();
        LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: Port threw an error <message: %s>", sstream.str().c_str());
        throw exception::read_exception(exception::read_exception::Code::PORT, sstream.str());
    } 
    return msglen;
}

int main() {
//set up port options (baudrate etc)
for(size_t i = 0; i<10; i++) {
std::vector<char> buffer;
readUntil(buffer, '\', std::chrono::microseconds(1000));
}

}

I shouldn't have thrown the error from the callback. This will work:

std::size_t RS485CommunicationLayer::readUntil(std::vector<char>& buffer, char delim, std::chrono::microseconds timeout) {
    using boost::system::error_code;
    boost::optional<error_code> read_result {};
    boost::optional<error_code> timer_result {};
    size_t msglen = 0;
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: start");
    io.reset();

    boost::asio::system_timer timer(io, timeout);
    boost::asio::async_read_until(port, boost::asio::dynamic_buffer(buffer), delim, [&](error_code ec, size_t n) {
            if(!timer_result) {
                read_result = ec;
                timer.cancel();
                msglen = n;
            } // else canceled, do nothing
        }
    );
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: read action set");
    timer.async_wait([&](error_code ec) { 
        if (!read_result) {
            timer_result = ec;
            port.cancel();
        } // else canceled, do nothing
    });
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: timer set : %d", static_cast<int>(timeout.count()));

    io.run();
    LOG_INFO_FORMAT_IF(rS485CommunicationLayerLogger, "RS485CommunicationLayer::readUntil :: end");

    if(timer_result) {
        if(timer_result.get()) {
            LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, 
                "RS485CommunicationLayer::readUntil() :: Timed out with error %s", 
                timer_result.get().message().c_str()
            );
        } else {
            LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, 
                "RS485CommunicationLayer::readUntil() :: Timed out"
            );
        }
        throw exception::read_exception(exception::read_exception::Code::TIME_OUT);
    } else if(read_result && read_result.get()) {
        std::stringstream sstream;
        sstream << read_result.get();
        LOG_ERROR_FORMAT_IF(rS485CommunicationLayerLogger, 
            "RS485CommunicationLayer::readUntil :: Port threw an error <message: %s>", 
            sstream.str().c_str());
        throw exception::read_exception(exception::read_exception::Code::PORT, sstream.str());
    }
    return msglen;
   // TODO Maybe throw error in the completely unexpected case if no optional is populated
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM