RyanChen.YLC
RyanChen.YLC

Reputation: 87

segfault when using boost::asio::async_read

I'm new in doing serial port communication with boost::asio.

In my program, the code can write data to the serial port correctly. But when receiving data, segmentation fault came up.

I tried std::cout to print a string to find the wrong parts, and found that problem may be caused by the deadline_timer (sp_timeout). But I have totally no idea how to deal with it...

Here is my code:

std::vector<char> SP::asyncRead(size_t rcv_size)
    {
        sp_service->reset();
        sp_is_read_timeout = false;
        sp_available = false;
        std::vector<char> sp_char(rcv_size);
        try
        {
            boost::mutex::scoped_lock sl(*sp_mutex);
            async_read( *sp_port, 
                        boost::asio::buffer(sp_char, rcv_size),
                        boost::bind(&SP::readCallback,
                                    this,
                                    boost::ref(*sp_timeout),
                                    boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred)
                        );
            std::cout << "now OK" << std::endl;
            sp_timeout->expires_from_now(boost::posix_time::millisec(1500));
            sp_timeout->async_wait(boost::bind( &SP::timeoutCallback, 
                                                this,
                                                boost::ref(*sp_port),
                                                boost::asio::placeholders::error)
                                   );
            sp_service->run(); 
        }
        catch(const std::exception& e)
            {
                std::cerr << e.what() << '\n';
            }
        if (sp_available)
        {
            return sp_char;
        }
        else
        {
            throw "Serial port timeout";
        }
    }

UPDATE:

Minimal reproducible example provided below:

main_node.cpp

#include "sp.h"
#include <iostream>
#include <thread>
#include <boost/thread/mutex.hpp>

std::shared_ptr<SP::SP> sp{ new SP::SP{"/dev/pts/6", 115200} };

int main(int argc, char **argv)
{
    sp->open();
    uint16_t _DATA = 0x0001;

    while(true){
        try
        {
            std::vector<char> rcv = sp->read_and_write(_DATA, 10);
            for(auto i=0; i<rcv.size(); i++){
                std::cout << std::hex << rcv[i] << std::endl;
            }
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        sleep(3);
    }
    sp->close();
    return 0;
}

sp.cpp

#include "sp.h"
namespace SP
{
    SP::SP(const std::string _serial_port, const int _baud_rate){
        set_param_serial(_serial_port, _baud_rate);
    }
    SP::~SP(){
        close();
    }

    void SP::set_param_serial(const std::string _serial_port, const int _baud_rate){
        sp_serial_port = _serial_port;
        sp_baud_rate = _baud_rate;
    }

    int SP::open(){
        if(sp_port != NULL){
            return -1;
        }

        if(sp_service){
            sp_port.reset();
            sp_service.reset();
        }
            
        sp_mutex = std::shared_ptr<boost::mutex>{new boost::mutex};
        sp_service = std::shared_ptr<io_service>{new io_service()};
        sp_port = std::shared_ptr<serial_port>{ new serial_port(*sp_service) };

        try 
        {
            sp_port->open(sp_serial_port);
            sp_port->set_option(serial_port_base::baud_rate(sp_baud_rate));
            sp_port->set_option(serial_port_base::character_size(8));
            sp_port->set_option(serial_port_base::parity(serial_port_base::parity::none));
            sp_port->set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
        }
        catch (std::exception &ex){
            std::cout << ex.what() << std::endl;
        }
        return 0;           
    }
    int SP::close(){
        if(sp_port && sp_port->is_open()){
             sp_port->close();
        }
        return 0;           
    }
    void SP::write(std::vector<char> _data){
        if (sp_port->is_open()){
            boost::mutex::scoped_lock lock(*sp_mutex);
            auto size = sp_port->write_some(buffer(_data));
            if(_data.size() != size){
                throw "Write Size Error!!!";
            }      
        }
        else
        {
            throw "Port not open";
        }
    }    
    void SP::write_data(uint16_t _data)
    {
        std::vector<uint8_t> sp_data;
        sp_data.clear();
        sp_data.push_back(_data >> 8);
        sp_data.push_back(_data);
        
        std::vector<char> sp_char(sp_data.begin(), sp_data.end());
        this->write(sp_char);
    }

    std::vector<char> SP::asyncRead(size_t rcv_size)
    {
        sp_service->reset();
        sp_is_read_timeout = false;
        sp_available = false;
        std::vector<char> sp_char(rcv_size);
        try
        {
            boost::mutex::scoped_lock sl(*sp_mutex);
            async_read( *sp_port, 
                        boost::asio::buffer(sp_char, rcv_size),
                        boost::bind(&SP::readCallback,
                                    this,
                                    boost::ref(*sp_timeout),
                                    boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred
                                    )
                        );
            sp_timeout->expires_from_now(boost::posix_time::millisec(1500));
            sp_timeout->async_wait(boost::bind(  &SP::timeoutCallback, 
                                                this,
                                                boost::ref(*sp_port),
                                                boost::asio::placeholders::error
                                                )
                                );
            sp_service->run(); 
            
        }
        catch(const std::exception& ex)
        {
            std::cout << ex.what() << std::endl;
        }
        if (sp_available)
        {
            return sp_char;
        }
        else
        {
            throw "Serial port reading timeout";
        }
    }

    void SP::readCallback(deadline_timer &timeout, const boost::system::error_code &error, std::size_t bytes_transferred)
    {
        if (error)
        {
            sp_available = false;
            std::cerr << "readCallback Error " << error << std::endl;
            return;
        }
        timeout.cancel();
        sp_available = true;
    }

    void SP::timeoutCallback(serial_port &s_port, const boost::system::error_code &error)
    {
        if (error)
        {
            return;
        }
        sp_is_read_timeout = true;
        sp_available = false;
        s_port.cancel(); 
        std::cerr << "Read timeout" << std::endl;
    }

    std::vector<char> SP::read_and_write(uint16_t _DATA, int num_bytes){
        const std::lock_guard<std::mutex> lock(sp_std_mutex);
        try
        {
            write_data(_DATA);
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        std::vector<char> response;
        {
            usleep(1500);
            try
            {
                response = asyncRead(num_bytes);
            }
            catch(const std::exception& e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        return response;
    }

}

sp.h

#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>

using namespace boost::asio;

namespace SP
{
    class SP
    {
        protected:
            std::shared_ptr<io_service> sp_service;
            std::shared_ptr<serial_port> sp_port;
            std::shared_ptr<boost::mutex> sp_mutex;
            std::shared_ptr<boost::mutex> sp_func_mutex;
            std::string sp_serial_port;
            int sp_baud_rate;
            
        public:
            SP(const std::string _serial_port, const int _baud_rate);
            virtual ~SP();

        public:
            void set_param_serial(const std::string _serial_port, const int _baud_rate);
            int open();
            int close();
            void write(std::vector<char> _data);
            void write_data(uint16_t _data);
            std::vector<char> asyncRead(size_t min_rcv);
            void readCallback(deadline_timer &timeout, const boost::system::error_code &error, std::size_t bytes_transferred);
            void timeoutCallback(serial_port &s_port, const boost::system::error_code &error);
            std::vector<char> read_and_write(uint16_t _DATA, int num_bytes);

        protected:
            bool sp_is_read_timeout;
            bool sp_available;
            std::shared_ptr<deadline_timer> sp_timeout;
            std::mutex sp_std_mutex;
    };
}

Upvotes: 2

Views: 526

Answers (1)

RyanChen.YLC
RyanChen.YLC

Reputation: 87

I think I found where the problem is...

In my program, I use deadline_timer to wait the data, but I only declare it in the sp.h file...

After finding this issue, I add

sp_timeout = std::shared_ptr<deadline_timer>{new deadline_timer(*sp_service)};

into the SP::open(), and it works.

It's a stupid problem. Sorry for wasting your time...

Upvotes: 2

Related Questions