Reputation: 2975
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
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