Search code examples
c++booststlboost-asioshared-ptr

std::vector::erase() (multithreaded) 'Assertion `px != 0' failed.'


Similar to shared_ptr Assertion px != 0 failed

I'm writing a game server that spawns a new thread to handle each user session. The main thread has a std::vector of UserSession shared pointers. Another thread periodically removes dead sessions from this vector but fails when performing std::vector::erase(). I can't find out what is wrong for the life of me.

The error is:

Prototype2: /usr/include/boost/smart_ptr/shared_ptr.hpp:653: typename boost::detail::sp_member_access::type boost::shared_ptr::operator->() const [with T = UserSession; typename boost::detail::sp_member_access::type = UserSession*]: Assertion `px != 0' failed. Aborted (core dumped)

The relevant code is:

void GameServer::start()
{
    int sessionid;
    boost::asio::io_service io_service;
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_));
    boost::thread(&GameServer::session_monitor, this);

    for (;;)
    {   
        socket_shptr socket(new tcp::socket(io_service));
        acceptor.accept(*socket);
        sessionid = numsessions_++;
        UserSession* usession = new 
            UserSession(socket, sessionid, io_service);
        session_shptr session(usession);

        sessions_mutex_.lock();
        sessions_.push_back(session);
        sessions_mutex_.unlock();

        std::cout << "Starting session for client " <<
            get_client_ip(*socket) << std::endl;

        session->start();
    }   
}

void GameServer::session_monitor()
{
    for (;;)
    {   
        boost::this_thread::sleep(boost::posix_time::seconds(10));
        std::cout << "Removing dead user sessions" << std::endl;

        sessions_mutex_.lock();
        for (std::vector<session_shptr>::iterator it = sessions_.begin();
            it != sessions_.end(); ++it)
        {
            if ((*it)->is_dead())
            {
                std::cout << "Removing session: " << (*it)->id() <<
                    std::endl;

                sessions_.erase(it);
            }
        }
        sessions_mutex_.unlock();
    }
}

Solution

  • Calling erase on an iterator invalidates it. You then attempt to continue to use it for iterating through the list. You need to use the return value of erase to continue iterating through the list.

        for (std::vector<session_shptr>::iterator it = sessions_.begin(); it != sessions_.end(); )
        {
            if ((*it)->is_dead())
            {
                std::cout << "Removing session: " << (*it)->id() <<
                    std::endl;
    
                it = sessions_.erase(it);
            }
            else
              ++it;
        }