Gaurav K
Gaurav K

Reputation: 2975

shared_from_this() is called after object pointing by this is destroyed: C++ ASIO

I am tryin to develop ASIO Application and have referred Chat-Server

When my CServer Object destructs it causes CSerSessionsManager Object to destruct- which holds shared pointer to all active chat sessions. It causes all active CSerCession Objects to destroy as well.

See the Definitions

class CServer {

    CServer(asio::io_service& io_service, const std::string serIdentity, std::string IP, const std::string port);
    ~CServer();
.....

private:

    mutable tcp::acceptor acceptor_; // only in the listener
    asio::io_service& io_;
    CSerSessionsManager mng_;

......

};


class CSerSessionsManager{
public:
    CSerSessionsManager();
    ~CSerSessionsManager();

    void addSession(sessionPtr session);
    void dropSession(sessionPtr session);

private:

    std::set<sessionPtr> sessions_; //Active Sessions : Online Info

};

class CSerSession : public std::enable_shared_from_this<CSerSession>{
    public:
        CSerSession(asio::io_service& io_service, CSerSessionsManager& mng, const std::string serverID,
            const std::string ip, const std::string port);
        ~CSerSession();

.......
    private:

        mutable tcp::socket socket_;   // client connection
        CSerSessionsManager& manager_;
......

    };

But since the CSerSession Object destroys it causes read error for active session and read_handle() is called.

void CSerSession::handle_read(const asio::error_code& error /*error*/, size_t bytes_transferred /*bytes_transferred*/)
{
    if (!error)
    {
        //do Something
    }
    else
    {
        DEBUG_MSG("Read Error Detected : " << error.message());
        //Check If shared_from_this() is valid or not
        try
        {

            //if (error == asio::error::operation_aborted)
            manager_.dropSession(shared_from_this()); //Exception Here
        }
        catch (const std::bad_weak_ptr& e)
        {
            DEBUG_MSG(e.what());
            throw e;
        }
        return;
    }
}

The code throws exception at Exception Here. On debugging it shows:

this    0x0044697c {socket_={...} manager_={sessions_={ size=??? } sessionPool_={ size=??? } } ip_=<Error reading characters of string.> ...}
std::enable_shared_from_this<channel::CSerSession>  {_Wptr={[deleter and allocator]={_Uses=??? _Weaks=??? } } }
socket_ {...}
manager_    {sessions_={ size=??? } sessionPool_={ size=??? } }
ip_ <Error reading characters of string.>
port_   <Error reading characters of string.>
parentServer_   <Error reading characters of string.>
servicedClientID_   <Error reading characters of string.>
serSessioID_    <Error reading characters of string.>
serSessionIdentifier_   <Error reading characters of string.>
privilege_  -274
serSessionIdentitySet_  true (238)
msg_    {received_=0x00446a77 "þîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþî... }
writeQueue_ { size=4277075694 }

I can see that manager_.dropSession(shared_from_this()); is being called when debugger shows std::enable_shared_from_this<channel::CSerSession> {_Wptr={[deleter and allocator]={_Uses=??? _Weaks=??? }.

It is being called after the destruction of CSerSession object as read_error is being set. Default behavior on error is to destroy the session and hence manager_.dropSession(shared_from_this()); is mandatory there.

The problem is that the destruction of this object causing read_error which again tries to destroy the same object via manager_.dropSession(shared_from_this());

How do I resolve the error?

EDIT: Summarizing the Issue:

Any error during chat session should set read error and read_handle() should be called which then destroys chat session via manager_.dropSession(shared_from_this());

But when my chat session CSerSession Object is going out of scope i.e. destructor is called, it is also causing read error as it was active. So again manager_.dropSession(shared_from_this()); is being called.

So basically shared_from_this() is being indirectly called from destructor.

Upvotes: 0

Views: 475

Answers (1)

GreenScape
GreenScape

Reputation: 7755

When you read from socket and set CSerSession::handle_read read handler, you should bind shared_from_this() instead of this.

For example, your async_read() should look similar to this:

socket_.async_read(boost::asio::buffer(...),
                   std::bind(&CSerSession::handle_read, shared_from_this(),
                             std::placeholder::_1, 
                             std::placeholder::_2);

The problem you observe is caused by the nature of event queue. When you destroy CSerSession some events in the event queue may keep this pointer to object that was already destructed. Thats why developers recommend using shared_ptr and shared_from_this(). When you pass shared_ptr instead of this you keep object of interest alive.

Response to your edit:

You are saying that manager_.dropSession(shared_from_this()); is being indirectly invoked from destructor. But how come object is being destructed, if read_handle() still keeps a reference it? Do you provide read_handle() with a shared_from_this() as I mentioned above?

Upvotes: 2

Related Questions