Search code examples
c++networkingboosttcpasio

Boost socket acceptor segmentation fault


Im trying write tcp server. From boost guide link enter link description here. When im trying open acceptor - segmentation fault, IDE show me this line object_pool_access::prev(live_list_) = o; What im doing wrong? Im trying another way for example run threads for context but that doesnt work. Sometime when im try to fix i get this one message -

tpp.c:82: __pthread_tpp_change_priority: Assertion `new_prio == -1 || (new_prio >= fifo_min_prio && new_prio <= fifo_max_prio)' failed.
Aborted (core dumped)

The code is contained in the 1st file. Application class with io_context, he is here because ill use him in another parts of program:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <memory>

using namespace boost::asio;

class api_application
{
public:
    virtual ~api_application() {}

    virtual void start() = 0;
    virtual boost::asio::io_context& get_context() = 0;
};

class application : public api_application
{
public:
    application();
    ~application() override {}

public:
    virtual void start() override;
    virtual boost::asio::io_context& get_context() override;
    
    void stop();

private:
    boost::asio::io_context _context;

    std::shared_ptr<tcp_server> _server;
};


application::application()
:   
_context(),
_server(std::make_shared<tcp_server>(*this))
{}

void application::start()
{
    _server->start();
    _context.run();
}

void application::stop()
{
    _context.stop();
}

boost::asio::io_context& application::get_context()
{
    return _context;
}

Networking code with error line:

class connection : public std::enable_shared_from_this<connection>
{
public:
    typedef std::shared_ptr<connection> con_ptr;

    static con_ptr create(boost::asio::io_context& io_service)
    {
        return con_ptr(new connection(io_service));
    }

    ip::tcp::socket& socket();
    void send(std::string message);

private:
    connection(boost::asio::io_context& io_service);

    void handle_write(const boost::system::error_code& err, size_t s);

private:
    ip::tcp::socket _socket;
};

class tcp_server
{
public:
    tcp_server(api_application& app);

    void start();
private:
    void start_accept();
    void handle_accept(connection::con_ptr new_connection, const boost::system::error_code& error);

    api_application&   _app;
    ip::tcp::endpoint       _endpoint;
    ip::tcp::acceptor       _acceptor;
};


tcp_server::tcp_server(api_application& app)
:
_app(app), 
_endpoint(boost::asio::ip::address_v4::any(), 80),
_acceptor(app.get_context())
{}

void tcp_server::start()
{
    if(_acceptor.is_open())
        return;
    
    _acceptor.open(_endpoint.protocol()); // Here segfault
    _acceptor.set_option(ip::tcp::socket::reuse_address(true));
    
    _acceptor.bind(_endpoint);
    _acceptor.listen();
    start_accept();
}

void tcp_server::start_accept()
{
    connection::con_ptr new_connection =
    connection::create((boost::asio::io_context&)_acceptor.get_executor().context());

    _acceptor.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
        boost::asio::placeholders::error));
}

void tcp_server::handle_accept(connection::con_ptr new_connection, const boost::system::error_code& error)
{
    if (!error)
    {
        new_connection->send("Success connection");
    }

    start_accept();
}

void connection::send(std::string message)
{
    boost::asio::async_write(_socket, boost::asio::buffer(message),
        boost::bind(&connection::handle_write, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}

connection::connection(boost::asio::io_context& io_service)
: _socket(io_service)
{}

int main(int argc, const char** argv)
{
    application app();
    app.start();
    return 0;
}

Solution

  • I found the likely issue while rewriting the code to avoid context references. See Update

    I have no problem with the code, imagining the missing bits:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <boost/bind/bind.hpp>
    #include <iostream>
    #include <memory>
    
    namespace asio = boost::asio;
    using asio::ip::tcp;
    using boost::system::error_code;
    
    class connection : public std::enable_shared_from_this<connection> {
      public:
        typedef std::shared_ptr<connection> con_ptr;
    
        static con_ptr create(asio::io_context& io_service) {
            return con_ptr{new connection(io_service)};
        }
    
        tcp::socket& socket() { return _socket; }
        void         send(std::string message);
    
      private:
        connection(asio::io_context& io_service);
    
        void handle_write(error_code err, size_t s) {
            std::cerr << "handle_write: " << err.message() << " " << s << std::endl;
        }
    
      private:
        tcp::socket _socket;
    };
    
    struct api_application {
        virtual ~api_application() {}
        virtual void              start()       = 0;
        virtual asio::io_context& get_context() = 0;
    };
    
    class tcp_server {
      public:
        tcp_server(api_application& app);
    
        void start();
    
      private:
        void start_accept();
        void handle_accept(connection::con_ptr new_connection,
                           error_code          error);
    
        api_application& _app;
        tcp::endpoint    _endpoint;
        tcp::acceptor    _acceptor;
    };
    
    class application : public api_application {
      public:
        application();
        ~application() override {}
    
      public:
        virtual void              start() override;
        virtual asio::io_context& get_context() override;
    
        void stop();
    
      private:
        asio::io_context _context;
    
        std::shared_ptr<tcp_server> _server;
    };
    
    application::application()
        : _context()
        , _server(std::make_shared<tcp_server>(*this)) {}
    
    void application::start() {
        _server->start();
        _context.run();
    }
    
    tcp_server::tcp_server(api_application& app)
        : _app(app)
        , _endpoint({}, 8989)
        , _acceptor(app.get_context()) {}
    
    void tcp_server::start() {
        if (_acceptor.is_open())
            return;
    
        _acceptor.open(_endpoint.protocol()); // Here segfault
        _acceptor.set_option(tcp::acceptor::reuse_address(true));
    
        _acceptor.bind(_endpoint);
        _acceptor.listen();
        start_accept();
    }
    
    void tcp_server::start_accept() {
        connection::con_ptr new_connection = connection::create(
            (asio::io_context&)_acceptor.get_executor().context());
    
        _acceptor.async_accept(new_connection->socket(),
                               boost::bind(&tcp_server::handle_accept, this,
                                           new_connection,
                                           asio::placeholders::error));
    }
    
    void tcp_server::handle_accept(connection::con_ptr new_connection,
                                   error_code          error) {
        if (!error) {
            new_connection->send("Success connection");
        }
    
        start_accept();
    }
    
    void connection::send(std::string message) {
        async_write(_socket, asio::buffer(message),
                    boost::bind(&connection::handle_write,
                                shared_from_this(),
                                asio::placeholders::error,
                                asio::placeholders::bytes_transferred));
    }
    
    connection::connection(asio::io_context& io_service)
        : _socket(io_service) {}
    
    void application::stop() { _context.stop(); }
    
    asio::io_context& application::get_context() { return _context; }
    
    int main() {
        application app;
        app.start();
    }
    

    Prints e.g.

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp
    ./a.out&
    for a in {1..10}; do sleep 0.5; nc 127.0.0.1 8989 <<<"Hello world"; done
    kill %1
    Success connectionhandle_write: Success 18
    handle_write: Success connectionSuccess 18
    handle_write: Success Success connection18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    Success connectionhandle_write: Success 18
    

    Summary

    Your problem is elsewhere. Permissions, exception handling, ODR, maybe you're not running the code you think you are.

    Also, this code seems unnecessarily complicated and a bit dated (io_service has been deprecated for quite some time).

    UPDATE

    This is highly suspicious code

    connection::con_ptr new_connection = connection::create( (asio::io_context&)_acceptor.get_executor().context());

    If it works it is due to undocumented implementation details. It is technically Undefined Behaviour because it does a hard re-interpret-cast. Instead, just use the executor as intended!

    auto new_connection = connection::create(_acceptor.get_executor());
    

    Here's the whole thing reworked to avoid context references:

    #include <boost/asio.hpp>
    #include <boost/bind/bind.hpp>
    #include <iostream>
    #include <memory>
    
    namespace asio = boost::asio;
    using asio::ip::tcp;
    using boost::system::error_code;
    
    class connection : public std::enable_shared_from_this<connection> {
      public:
        typedef std::shared_ptr<connection> con_ptr;
    
        static con_ptr create(asio::any_io_executor ex) {
            return con_ptr{new connection(ex)};
        }
    
        tcp::socket& socket() { return _socket; }
        void         send(std::string message);
    
      private:
        connection(asio::any_io_executor ex);
    
        void handle_write(error_code err, size_t s) {
            std::cerr << "handle_write: " << err.message() << " " << s << std::endl;
        }
    
      private:
        tcp::socket _socket;
    };
    
    struct api_application {
        virtual ~api_application() {}
        virtual void                  start()        = 0;
        virtual asio::any_io_executor get_executor() = 0;
    };
    
    class tcp_server {
      public:
        tcp_server(api_application& app);
    
        void start();
    
      private:
        void start_accept();
        void handle_accept(connection::con_ptr new_connection,
                           error_code          error);
    
        api_application& _app;
        tcp::endpoint    _endpoint;
        tcp::acceptor    _acceptor;
    };
    
    class application : public api_application {
      public:
        application();
        ~application() override {}
    
      public:
        virtual void                  start() override;
        virtual asio::any_io_executor get_executor() override;
    
        void stop();
    
      private:
        asio::io_context _context;
    
        std::shared_ptr<tcp_server> _server;
    };
    
    application::application()
        : _context()
        , _server(std::make_shared<tcp_server>(*this)) {}
    
    void application::start() {
        _server->start();
        _context.run();
    }
    
    tcp_server::tcp_server(api_application& app)
        : _app(app)
        , _endpoint({}, 8989)
        , _acceptor(app.get_executor()) {}
    
    void tcp_server::start() {
        if (_acceptor.is_open())
            return;
    
        _acceptor.open(_endpoint.protocol()); // Here segfault
        _acceptor.set_option(tcp::acceptor::reuse_address(true));
    
        _acceptor.bind(_endpoint);
        _acceptor.listen();
        start_accept();
    }
    
    void tcp_server::start_accept() {
        auto new_connection = connection::create(_acceptor.get_executor());
    
        _acceptor.async_accept(new_connection->socket(),
                               boost::bind(&tcp_server::handle_accept, this,
                                           new_connection,
                                           asio::placeholders::error));
    }
    
    void tcp_server::handle_accept(connection::con_ptr new_connection,
                                   error_code          error) {
        if (!error) {
            new_connection->send("Success connection");
        }
    
        start_accept();
    }
    
    void connection::send(std::string message) {
        async_write(_socket, asio::buffer(message),
                    boost::bind(&connection::handle_write,
                                shared_from_this(),
                                asio::placeholders::error,
                                asio::placeholders::bytes_transferred));
    }
    
    connection::connection(asio::any_io_executor ex) : _socket(ex) {}
    
    void application::stop() { _context.stop(); }
    asio::any_io_executor application::get_executor() {
        return _context.get_executor();
    }
    
    int main() {
        application app;
        app.start();
    }
    

    Still printing the same Live On Coliru