Search code examples
c++11boost-asioshared-ptrcoredump

Understanding issue with shared pointers (Lifetime, passing as parameter)


I tried to start with the boost asio chat example and derive an own networking program. Unfortunately I have some problem understanding what really happens. I tried to reduce my program to an absolute minimum. A server class waits for incoming connections and creates a session object to handle the connection. This is the code of the server:

#include <cstdint>
#include <iostream>
#include <sstream>
#include <memory>
#include <vector>

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

class Session : public std::enable_shared_from_this<Session>
{
public:
    Session(boost::asio::ip::tcp::socket __oSocket);
    virtual ~Session();
    void StartSession();
private:
    void StartRecv();
    std::vector<int32_t> m_vecSetupReceiveBuffer;
    boost::asio::ip::tcp::socket m_oSocket;
};

Session::Session(boost::asio::ip::tcp::socket __oSocket) :
    m_vecSetupReceiveBuffer({2}),
    m_oSocket(std::move(__oSocket))
{    }

Session::~Session()
{
    std::cout << "Deleted session" << std::endl;
}

void Session::StartSession()
{
    auto self(shared_from_this());
    std::cout << "StartSession()" << std::endl;
    boost::asio::async_write(m_oSocket, boost::asio::buffer(m_vecSetupReceiveBuffer),
    [this, self](boost::system::error_code _oError, std::size_t)
    {
        std::cout << m_vecSetupReceiveBuffer.size() << std::endl;
        StartRecv();
    });
}

void Session::StartRecv()
{
    auto self(shared_from_this());
    std::cout << "StartRecv()" << std::endl;
    boost::asio::async_read(m_oSocket, boost::asio::buffer(m_vecSetupReceiveBuffer),
    [this, self](boost::system::error_code _oError, std::size_t)
    {});
}

class Server
{
public:
    Server(boost::asio::io_service& _rIOService, uint32_t _nPort);
    virtual ~Server();
private:
    void StartAccept();
    boost::asio::ip::tcp::acceptor m_oAcceptor;
    boost::asio::ip::tcp::socket m_oSocket;
};

Server::Server(boost::asio::io_service& _rIOService, uint32_t _nPort) :
    m_oAcceptor(_rIOService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), _nPort)),
    m_oSocket(_rIOService)
{
    StartAccept();
}

Server::~Server()
{}

void Server::StartAccept()
{
    m_oAcceptor.async_accept(m_oSocket,
    [this](boost::system::error_code _oError)
    {
        std::make_shared<Session>(std::move(m_oSocket))->StartSession();
        StartAccept();
    });
}


int main(int argc, char* argv[])
{
    boost::asio::io_service _oIOServerService;
    std::shared_ptr<Server> _pServer(std::make_shared<Server>(_oIOServerService, 2000));
    _oIOServerService.run();
    return 0;
}

This code runs as intended, but as I tried to adjust some things and to play a little bit around I figured out that I don't understand when shared pointers are really created and deleted and where they are stored. For example I tried to change the std::make_shared<Session>(std::move(m_oSocket))->StartSession(); to std::make_shared<Session>(std::move(m_oSocket)); and added the StartSession(); in the constructor of the Session class. If I run the code then, it throws

terminate called after throwing an instance of 'std::bad_weak_ptr'
what():  bad_weak_ptr
Aborted (core dumped)

This presumably happens in auto self(shared_from_this());. But I don't understand why? What should I change in my code? So I think my problem is that I don't understand how to use the shared_ptr correctly, how to use it in these constructions, where I can access it and how to make it accessible. Further it is not clear why I sometimes have to user this and when to use shared_from_this(). Is there a good tutorial, or a simple rule of thumb?

It is also unclear to me, why some user the lambda function notation, and some the boost::bind notation and what are the differences?

So please forgive my newbie questions, I tried to find some information in tutorials but am only confused with these shared pointers and boost::asio. It seems always to do strange things.


Solution

  • You get std::bad_weak_ptr because there is no shared_ptr owning this during the construction of your Session. You are still in the body of make_shared, and it hasn't finished creating the shared_ptr.

    shared_from_this() is only available from the instance of Session, it is a member function. You have inherited it by deriving from std::enable_shared_from_this.

    The lambdas are constructed with copies of self, and the members of Session are available as-if this also pointed to the Session object, not the lambda object, because of the this capture