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.
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.
connection_manager
creates and stores objects of type std::shared_ptr<worker_thread>
inside a std::vector
containerworker_thread
inherits from std::enable_shared_from_this<worker_thread>
.worker_thread
creates objects of type std::shared_ptr<connection>
.connection
requires a pointer (which is a shared pointer) to the class worker_thread
, so that in can call the void handle_finish(std::shared_ptr<connection>)
worker_thread
is created via its constructor, from the class connection_manager
using std::make_shared<worker_thread>
with two shared pointers as parameters.void init()
is called from worker_thread
by connection_manager
connection_manager
calls std::shared_ptr<connection> get_available_connection()
from worker_thread
connection
is created via std::make_shared<connection>
, and one of the arguments is the shared pointer to the current worker_thread
obtained via shared_from_this()
shared_from_this()
call, the program crashes with a bad_weak_ptr
exception.From my research, the most common causes of this error are:
shared_from_this()
is called within a constructor (or a function which is called by the constructor)std::shared_ptr
pointing to the object.In my program:
get_available_connection()
are separate, and through outputing lines in the terminal, it seems that the worker_thread
is constructed and initialised by the time the call to get_available_connection()
occursconnection_manager
class holds a shared pointer to every worker_thread
object.All something_ptr
are 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;
}
}
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.
Google Drive link
I think your problem might be that you use default private
inheritance.
here is a simple example of a program that crashes:
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 inheritance
{
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With