skip to Main Content

I have the following code which encompasses a boost::mysql connection using a shared_ptr. I want to keep the connection object for the duration of the application’s runtime. I want the application to then close the connection on the end of the runtime. However, I get an access violation when debugging the process.

class DatabaseProcess : public MainframeComponent
{
public:
    // MainframeComponent dispatch module type is set to Main because DatabaseProcess doesn't run on its own thread.
    DatabaseProcess(boost::asio::io_context &io_context) : _io_context(io_context), MainframeComponent(Horizon::System::RUNTIME_DATABASE) { }
    ~DatabaseProcess() 
    { 
        // Close connection object.
        if (_connection != nullptr) {
            _connection->close();
            _connection.reset();
        }

    }
    void initialize(int segment_number = 1) override { HLog(error) << "Database not configured"; }
    void initialize(int segment_number, std::string host, int port, std::string user, std::string pass, std::string database)
    {
        try {
            boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
            _connection = std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), ssl_ctx);
            boost::asio::ip::tcp::resolver resolver(_io_context.get_executor());
            auto endpoints = resolver.resolve(host, std::to_string(port));
            boost::mysql::handshake_params params(user, pass, database);
            _connection->connect(*endpoints.begin(), params);
        } catch (boost::mysql::error_with_diagnostics &error) {
            HLog(error) << error.what();
        }

        bool value = _is_initialized;
        _is_initialized.compare_exchange_strong(value, true);
    }

    void finalize(int segment_number = 1) override 
    {
        bool value = _is_initialized;
        _is_initialized.compare_exchange_strong(value, false);
    }

    std::shared_ptr<boost::mysql::tcp_ssl_connection> get_connection() { return _connection; }

    bool is_initialized() { return _is_initialized.load(); }
protected:
    std::shared_ptr<boost::mysql::tcp_ssl_connection> _connection{nullptr};
    boost::asio::io_context &_io_context;
    std::atomic<bool> _is_initialized;
};

Debug output:


Running 1 test case...

*** No errors detected
Exception thrown at 0x00007FFA63663FAA (ntdll.dll) in ZoneSystemTest.exe: 0xC0000005: Access violation writing location 0x0000000000000024.
The program '[27192] ZoneSystemTest.exe' has exited with code 0 (0x0).

The lines responsible significantly are –

_connection = std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), ssl_ctx);

When commented, the error goes away. Is it because we can’t create a resource in memory of the connection object?

2

Answers


  1. Chosen as BEST ANSWER

    The io_context belonged to a class which was the child of the class that holds the DatabaseProcess. So the answer was to change the io_context to be a member of that class and making sure that the destruction of DatabaseProcess occurs before the destruction of that class.


  2. The lifetimes of objects are not guarded well.

    • the _io_context member is initialized AFTER the base class, you want to fix the order of the initializers to reflect this

    • the ssl_ctx is destructed before the end of initialize. It probably needs to be a member object

    • you probably want to use all the resolver endpoints in the connection attempt:

           boost::asio::connect(_connection->stream().next_layer(), endpoints);
      
    • most importantly, the lifetime of the io_context object is not shown, but needs to be guarded.

    Everything could be greatly simplified by avoiding references, two-step initialization and dynamic allocation.

    Some Fixes

    Some of the above fixed:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <boost/mysql.hpp>
    
    #include <iostream>
    #include <syncstream>
    #define HLog(level) (std::osyncstream(std::cout) << "n[" #level "] ")
    
    namespace Horizon::System {
        enum ModuleType {
            RUNTIME_DATABASE,
            RUNTIME_FILESYSTEM,
            RUNTIME_NETWORK,
            RUNTIME_PROCESS,
            RUNTIME_THREAD,
            RUNTIME_TIMER,
            RUNTIME_USER
        };
    }
    
    namespace asio = boost::asio;
    namespace ssl  = asio::ssl;
    using asio::ip::tcp;
    
    struct MainframeComponent {
        MainframeComponent(Horizon::System::ModuleType module_type) : _module_type(module_type) {}
    
        virtual ~MainframeComponent()                   = default;
        virtual void initialize(int segment_number = 1) = 0;
        virtual void finalize(int segment_number = 1)   = 0;
    
        Horizon::System::ModuleType get_module_type() { return _module_type; }
    
      protected:
        Horizon::System::ModuleType _module_type;
    };
    
    class DatabaseProcess : public MainframeComponent {
      public:
        // MainframeComponent dispatch module type is set to Main because DatabaseProcess doesn't run on its own
        // thread.
        DatabaseProcess(asio::io_context& io_context)
            : MainframeComponent(Horizon::System::RUNTIME_DATABASE)
            , _io_context(io_context) {}
    
        ~DatabaseProcess() {
            // Close connection object.
            if (_connection != nullptr) {
                _connection->close();
                _connection.reset();
            }
        }
    
        void initialize(int /*segment_number*/ = 1) override { HLog(error) << "Database not configured"; }
        void initialize(int /*segment_number*/, std::string host, int port, std::string user, std::string pass,
                        std::string database) {
            try {
                _connection =
                    std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), ssl_ctx);
                tcp::resolver                  resolver(_io_context.get_executor());
                auto                           endpoints = resolver.resolve(host, std::to_string(port));
                boost::mysql::handshake_params params(user, pass, database);
                // asio::connect(_connection->stream().next_layer(), endpoints);
                _connection->connect(*endpoints.begin(), params);
            } catch (boost::mysql::error_with_diagnostics& error) {
                HLog(error) << error.what();
            }
    
            bool value = _is_initialized;
            _is_initialized.compare_exchange_strong(value, true);
        }
    
        void finalize(int /*segment_number*/ = 1) override {
            bool value = _is_initialized;
            _is_initialized.compare_exchange_strong(value, false);
        }
    
        std::shared_ptr<boost::mysql::tcp_ssl_connection> get_connection() { return _connection; }
    
        bool is_initialized() { return _is_initialized.load(); }
    
      protected:
        asio::io_context&                                 _io_context;
        ssl::context                                      ssl_ctx{ssl::context::tls_client};
        std::shared_ptr<boost::mysql::tcp_ssl_connection> _connection{nullptr};
        std::atomic<bool>                                 _is_initialized;
    };
    
    int main() {
        //
        asio::io_context ioc;
        DatabaseProcess p(ioc);
    
        p.initialize(1, "localhost", 3306, "root", "root", "test");
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search