I am creating a C++ server application using standalone Asio and C++11 and am getting an error, which is why I am asking for help.
The error
In the class worker_thread, during the call to shared_from_this(), a bad_weak_ptr exception is raised, which causes the program to crash.
The layout
- The class connection_managercreates and stores objects of typestd::shared_ptr<worker_thread>inside astd::vectorcontainer
- The class worker_threadinherits fromstd::enable_shared_from_this<worker_thread>.
- The class worker_threadcreates objects of typestd::shared_ptr<connection>.
- The class connectionrequires a pointer (which is a shared pointer) to the classworker_thread, so that in can call thevoid handle_finish(std::shared_ptr<connection>)
Program flow
- The class worker_threadis created via its constructor, from the classconnection_managerusingstd::make_shared<worker_thread>with two shared pointers as parameters.
- void init()is called from- worker_threadby- connection_manager
- Later in the program, connection_managercallsstd::shared_ptr<connection> get_available_connection()fromworker_thread
- During this method's execution, a new connectionis created viastd::make_shared<connection>, and one of the arguments is the shared pointer to the currentworker_threadobtained viashared_from_this()
- During the shared_from_this()call, the program crashes with abad_weak_ptrexception.
Research
From my research, the most common causes of this error are:
- When shared_from_this()is called within a constructor (or a function which is called by the constructor)
- When there is no existing std::shared_ptrpointing to the object.
In my program:
- The call to the constructor and the get_available_connection()are separate, and through outputing lines in the terminal, it seems that theworker_threadis constructed and initialised by the time the call toget_available_connection()occurs
- The connection_managerclass holds a shared pointer to everyworker_threadobject.
Code
All something_ptr are std::shared_ptr<something>
Header files
connection_manager.hpp
typedef asio::executor_work_guard<asio::io_context::executor_type>
    io_context_work;
std::vector<worker_thread_ptr> workers;
std::vector<io_context_ptr> io_contexts;
std::vector<io_context_work> work;
worker_thread.hpp
class worker_thread : std::enable_shared_from_this<worker_thread> {
public:
/// Create a worker thread.
explicit worker_thread(io_context_ptr io, config_ptr vars_global);
void init();
void join();
connection_ptr get_available_connection();
//...
connection.hpp
explicit connection(std::shared_ptr<worker_thread> worker,
            std::shared_ptr<asio::io_context> io, 
            config_ptr vars_parent);
Source files
connection_manager.cpp
connection_manager::connection_manager(config_ptr vars) {
    std::size_t number_of_threads = vars->worker_threads;
    while(number_of_threads > 0) {
        io_context_ptr io_context(new asio::io_context);
        io_contexts.push_back(io_context);
        work.push_back(asio::make_work_guard(*io_context));
        worker_thread_ptr worker =
            std::make_shared<worker_thread>(io_context, vars);
        workers.push_back(worker);
        worker->init();
        --number_of_threads;
    }   
} 
connection_ptr connection_manager::get_available_connection() {
    std::size_t index_of_min_thread = 0;
    std::size_t worker_count = workers.size();
    for(std::size_t i = 1; i < worker_count; ++i) {
        if(workers[i]->active_connection_count() <
                workers[index_of_min_thread]->active_connection_count())
            index_of_min_thread = i;
    }
    return workers[index_of_min_thread]->get_available_connection();
}
worker_thread.cpp
worker_thread::worker_thread(io_context_ptr io, 
        config_ptr vars_global)
    :io_context(io), active_conn_count(0), vars(vars_global),
    worker(
        [this]() {
            if(io_context)
                io_context->run();
        }   
    ) {}
void worker_thread::init() {
    //Additional initialisation, this is called by connection_manager
    //after this thread's construction
}   
connection_ptr worker_thread::get_available_connection() {
    connection_ptr conn;
    if(!available_connections.empty()) {
        conn = available_connections.front();
        available_connections.pop();
        active_connections.insert(conn);
        return conn;
    } else {
        conn = std::make_shared<connection>(shared_from_this(), io_context, vars);
        active_connections.insert(conn);
        return conn;
    }
}
I am sorry if this question has been answered before, but I tried to resolve this, and after trying for some time, I decided it would be better to ask for help.
EDIT Here is a minimum test, which fails. It requires CMake, and you might have to change the minimum required version.
 
     
    