Fixed design problem: Thread objects can now be created in any context.

Sender thread output queue is now limited.
Server thread now terminates correctly.
Now cloning packets when sending them to multiple recipients.
Fixed one thread issue - GameStart still needs to be made thread safe.
This commit is contained in:
lotodore
2007-03-20 11:56:30 +00:00
parent 53c4af95c8
commit 804c7eaaa2
12 changed files with 160 additions and 55 deletions
+74 -37
View File
@@ -44,10 +44,14 @@ private:
ServerRecvThread::ServerRecvThread()
{
m_senderCallback.reset(new ServerSenderCallback(*this));
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
}
ServerRecvThread::~ServerRecvThread()
{
CleanupConnectQueue();
CleanupSessionMap();
}
void
@@ -58,16 +62,18 @@ ServerRecvThread::StartGame()
}
void
ServerRecvThread::SendToAllClients(boost::shared_ptr<NetPacket> packet)
ServerRecvThread::SendToAllPlayers(boost::shared_ptr<NetPacket> packet)
{
// TODO: possible race condition if used in multithreading
SocketSessionMap::iterator i = m_sessions.begin();
SocketSessionMap::iterator end = m_sessions.end();
// This function needs to be thread safe.
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end)
{
// TODO: desparately need clone here, this is very dangerous.
GetSender().Send(packet, i->first);
// Send each client a copy of the packet.
GetSender().Send(boost::shared_ptr<NetPacket>(packet->Clone()), i->first);
++i;
}
}
@@ -83,9 +89,6 @@ void
ServerRecvThread::Main()
{
SetState(SERVER_INITIAL_STATE::Instance());
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
GetSender().Run();
try
@@ -114,7 +117,8 @@ ServerRecvThread::Main()
GetSender().SignalTermination();
GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT);
// TODO: clear connection queue
CleanupConnectQueue();
CleanupSessionMap();
}
SOCKET
@@ -122,31 +126,32 @@ ServerRecvThread::Select()
{
SOCKET retSock = INVALID_SOCKET;
if (m_sessions.empty())
SOCKET maxSock = INVALID_SOCKET;
fd_set rdset;
FD_ZERO(&rdset);
{
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end)
{
SOCKET tmpSock = i->first;
FD_SET(tmpSock, &rdset);
if (tmpSock > maxSock)
maxSock = tmpSock;
++i;
}
}
if (maxSock == INVALID_SOCKET)
{
Msleep(RECV_TIMEOUT_MSEC); // just sleep if there is no session
}
else
{
// wait for data
SOCKET maxSock = 0;
fd_set rdset;
FD_ZERO(&rdset);
{
SocketSessionMap::iterator i = m_sessions.begin();
SocketSessionMap::iterator end = m_sessions.end();
while (i != end)
{
SOCKET tmpSock = i->first;
FD_SET(tmpSock, &rdset);
if (tmpSock > maxSock)
maxSock = tmpSock;
++i;
}
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = RECV_TIMEOUT_MSEC * 1000;
@@ -158,8 +163,9 @@ ServerRecvThread::Select()
if (selectResult > 0) // one (or more) of the sockets is readable
{
// Check which socket is readable, return the first.
SocketSessionMap::iterator i = m_sessions.begin();
SocketSessionMap::iterator end = m_sessions.end();
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end)
{
@@ -176,6 +182,34 @@ ServerRecvThread::Select()
return retSock;
}
void
ServerRecvThread::CleanupConnectQueue()
{
boost::mutex::scoped_lock lock(m_connectQueueMutex);
// Sockets will be closed automatically.
m_connectQueue.clear();
}
void
ServerRecvThread::CleanupSessionMap()
{
boost::mutex::scoped_lock lock(m_sessionMapMutex);
// We need to manually close all sockets for the sessions.
// This is "not great", but there are some issues when
// automatically closing them.
SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end)
{
CLOSESOCKET(i->first);
++i;
}
m_sessionMap.clear();
}
ServerRecvState &
ServerRecvThread::GetState()
{
@@ -193,9 +227,10 @@ boost::shared_ptr<SessionData>
ServerRecvThread::GetSession(SOCKET sock)
{
boost::shared_ptr<SessionData> tmpSession;
SocketSessionMap::iterator pos = m_sessions.find(sock);
if (pos != m_sessions.end())
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator pos = m_sessionMap.find(sock);
if (pos != m_sessionMap.end())
{
tmpSession = pos->second;
}
@@ -205,15 +240,17 @@ ServerRecvThread::GetSession(SOCKET sock)
void
ServerRecvThread::AddSession(boost::shared_ptr<ConnectData> connData, boost::shared_ptr<SessionData> sessionData)
{
SocketSessionMap::iterator pos = m_sessions.lower_bound(connData->GetSocket());
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator pos = m_sessionMap.lower_bound(connData->GetSocket());
// If pos points to a pair whose key is equivalent to the socket, this handle
// already exists within the list.
if (pos != m_sessions.end() && connData->GetSocket() == pos->first)
if (pos != m_sessionMap.end() && connData->GetSocket() == pos->first)
{
throw ServerException(ERR_SOCK_CONN_EXISTS, 0);
}
m_sessions.insert(pos, SocketSessionMap::value_type(connData->ReleaseSocket(), sessionData));
m_sessionMap.insert(pos, SocketSessionMap::value_type(connData->ReleaseSocket(), sessionData));
}
SenderThread &