I’m having some problems attempting to create a multi-threaded Server. Everything works fine until I need to remove a client from the server.
The server is run in it’s own thread, then each client has it’s own thread as well.
I’m using boost::thread for all the threads. When I need to stop the client I call
void StopClient()
{
assert(mThread);
mStopMutex.lock();
mStopRequested = true;
mStopMutex.unlock();
shutdown(mSocket,2);
mThread->join();
}
Adding a breakpoint to the line
shutdown(mSocket,2);
I can see that mThread doesn’t exist! Does this mean that the thread has already exited? Do you always need to call join() for a boost::thread?
If I allow the code to run I get an access violation error.
Update
ServerThread
void StartServer()
{
assert(!mListenThread);
mListenThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&ServerThread::Listen, this)));
mUpdateThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&ServerThread::Update, this)));
}
void StopServer()
{
assert(mListenThread);
mStopRequested = true;
mMutex.lock();
for(int i = 0; i < mClients.size(); i++ )
mClients[i]->StopClient();
mMutex.unlock();
mListenThread->join();
}
void Listen()
{
while (!mStopRequested)
{
std::cout << "Waiting for connection" << std::endl;
if(mClientSocket = accept( mServerSocket, (sockaddr*) &mServerAddr, &addrlen ) )
{
mMutex.lock();
if( mClients.size() > 0 )
{
for( int i = 0; i < mClients.size(); i++ )
{
if( mClients[i]->getClientSocket() != mClientSocket )
{
ClientThread newClient;
newClient.Initialise(mClientSocket);
mClients.push_back(&newClient);
mClients[mClients.size()-1]->StartClient();
break;
}
}
}
else
{
ClientThread newClient;
newClient.Initialise(mClientSocket);
mClients.push_back(&newClient);
mClients[mClients.size()-1]->StartClient();
}
mMutex.unlock();
}
}
}
void Update()
{
while (!mStopRequested)
{
mMutex.lock();
std::cout << "::::Server is updating!::::" << mClients.size() << std::endl;
for( int i = 0; i< mClients.size(); i++ )
{
if( !mClients[i]->IsActive() )
{
mClients[i]->StopClient();
mClients.erase( mClients.begin() + i );
}
}
mMutex.unlock();
}
}
ClientThread
void StartClient()
{
assert(!mThread);
mThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&ClientThread::Update, this)));
}
void Update()
{
bool stopRequested;
do
{
mStopMutex.lock();
stopRequested = mStopRequested;
mStopMutex.unlock();
std::cout << "lol" << std::endl;
if( mTimeOut < 1000 )
{
mTimeOut++;
}
else
{
mActive = false;
}
boost::this_thread::interruption_point();
}
while( !stopRequested);
}
This creates a local variable on the stack, and puts the address of it into your list of mClients. Then the scope ends, and so does the local variable. This leaves your list
mClientspointing at something that’s not there anymore.