shared_from_this () вызывает std :: bad_weak_ptr даже при правильном использовании make_shared - PullRequest
0 голосов
/ 13 мая 2018

Я создаю серверное приложение C ++ с использованием автономных Asio и C ++ 11 и получаю сообщение об ошибке, поэтому прошу помощи.

Ошибка

В классе worker_thread во время вызова shared_from_this() возникает исключение bad_weak_ptr, которое вызывает сбой программы.

Макет

  1. Класс connection_manager создает и хранит объекты типа std::shared_ptr<worker_thread> внутри std::vector контейнера
  2. Класс worker_thread наследуется от std::enable_shared_from_this<worker_thread>.
  3. Класс worker_thread создает объекты типа std::shared_ptr<connection>.
  4. Класс connection требует указатель (который является общим указателем) на класс worker_thread, чтобы он мог вызывать void handle_finish(std::shared_ptr<connection>)

Программный поток

  1. Класс worker_thread создается через его конструктор, из класса connection_manager, используя std::make_shared<worker_thread> с двумя общими указателями в качестве параметров.
  2. void init() вызывается с worker_thread по connection_manager
  3. Позже в программе connection_manager звонит std::shared_ptr<connection> get_available_connection() с worker_thread
  4. Во время выполнения этого метода новый connection создается с помощью std::make_shared<connection>, и одним из аргументов является общий указатель на current worker_thread, полученный с помощью shared_from_this()
  5. Во время вызова shared_from_this() программа вылетает с исключением bad_weak_ptr.

Исследования

Из моего исследования, наиболее распространенные причины этой ошибки:

  1. Когда shared_from_this() вызывается внутри конструктора (или функции, которая вызывается конструктором)
  2. Когда не существует std::shared_ptr, указывающего на объект.

В моей программе:

  1. Вызов конструктору и get_available_connection() являются отдельными, и через вывод строк в терминале создается впечатление, что worker_thread создается и инициализируется к тому времени, когда происходит вызов get_available_connection()
  2. Класс connection_manager содержит общий указатель на каждый worker_thread объект.

Код

Все something_ptr являются std::shared_ptr<something>

Заголовочные файлы

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);

Исходные файлы

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;
    }
}

Прошу прощения, если на этот вопрос уже был дан ответ, но я попытался решить эту проблему, и, попробовав какое-то время, я решил, что лучше обратиться за помощью.

EDIT Вот минимальный тест, который не проходит. Требуется CMake, и вам, возможно, придется изменить минимально требуемую версию .

Ссылка на Google Диск

1 Ответ

0 голосов
/ 14 мая 2018

Я думаю, что ваша проблема в том, что вы используете наследование по умолчанию private.

Вот простой пример программы, которая дает сбой:

class GoodUsage : public std::enable_shared_from_this<GoodUsage>
{
public:
    void DoSomething()
    {
        auto good = shared_from_this();
    }
};

class BadUsage : std::enable_shared_from_this<BadUsage> // private inheritence
{
public:
    void DoSomething()
    {
        auto bad = shared_from_this();
    }
};


int main()
{
    auto good = std::make_shared<GoodUsage>();
    auto bad = std::make_shared<BadUsage>();
    good->DoSomething(); // ok
    bad->DoSomething(); // throws std::bad_weak_ptr    
}
...