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
+11 -5
View File
@@ -47,7 +47,6 @@ private:
}; };
Thread::Thread() Thread::Thread()
: m_userReqTerminateLock(m_shouldTerminateMutex), m_threadStartBarrier(2)
{ {
} }
@@ -58,13 +57,18 @@ Thread::~Thread()
void void
Thread::Run() Thread::Run()
{ {
// Create the boost thread object.
boost::mutex::scoped_lock threadLock(m_threadObjMutex); boost::mutex::scoped_lock threadLock(m_threadObjMutex);
// Create the boost thread object.
if (!m_threadObj.get()) if (!m_threadObj.get())
{ {
// Initialise data structures within the context of the thread
// who runs/terminates this thread.
m_userReqTerminateLock.reset(new boost::timed_mutex::scoped_try_lock(m_shouldTerminateMutex));
m_threadStartBarrier.reset(new boost::barrier(2));
m_threadObj.reset(new boost::thread(ThreadStarter(*this))); m_threadObj.reset(new boost::thread(ThreadStarter(*this)));
m_threadStartBarrier.wait(); m_threadStartBarrier->wait();
} }
} }
@@ -72,7 +76,8 @@ void
Thread::SignalTermination() Thread::SignalTermination()
{ {
// Unlock the shouldTerminateMutex. // Unlock the shouldTerminateMutex.
m_userReqTerminateLock.unlock(); if (m_userReqTerminateLock.get()) // cannot signal before calling Run
m_userReqTerminateLock->unlock();
} }
bool bool
@@ -118,7 +123,8 @@ void
Thread::MainWrapper() Thread::MainWrapper()
{ {
boost::timed_mutex::scoped_lock lock(m_isTerminatedMutex); boost::timed_mutex::scoped_lock lock(m_isTerminatedMutex);
m_threadStartBarrier.wait(); assert(m_threadStartBarrier.get());
m_threadStartBarrier->wait();
this->Main(); this->Main();
} }
+2 -2
View File
@@ -72,13 +72,13 @@ private:
// Flag specifying whether the thread should be terminated. // Flag specifying whether the thread should be terminated.
mutable boost::timed_mutex m_shouldTerminateMutex; mutable boost::timed_mutex m_shouldTerminateMutex;
mutable boost::timed_mutex::scoped_try_lock m_userReqTerminateLock; mutable boost::shared_ptr<boost::timed_mutex::scoped_try_lock> m_userReqTerminateLock;
// The boost thread object. // The boost thread object.
boost::shared_ptr<boost::thread> m_threadObj; boost::shared_ptr<boost::thread> m_threadObj;
mutable boost::mutex m_threadObjMutex; mutable boost::mutex m_threadObjMutex;
boost::barrier m_threadStartBarrier; mutable boost::shared_ptr<boost::barrier> m_threadStartBarrier;
friend class ThreadStarter; friend class ThreadStarter;
}; };
-1
View File
@@ -159,7 +159,6 @@ void
ClientStateResolving::SetResolver(ResolverThread *resolver) ClientStateResolving::SetResolver(ResolverThread *resolver)
{ {
Cleanup(); Cleanup();
m_resolver = resolver; m_resolver = resolver;
} }
+2 -2
View File
@@ -55,6 +55,8 @@ ClientThread::ClientThread(ClientCallback &cb)
{ {
m_context.reset(new ClientContext); m_context.reset(new ClientContext);
m_senderCallback.reset(new ClientSenderCallback(*this)); m_senderCallback.reset(new ClientSenderCallback(*this));
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
} }
ClientThread::~ClientThread() ClientThread::~ClientThread()
@@ -86,8 +88,6 @@ ClientThread::Main()
{ {
SetState(CLIENT_INITIAL_STATE::Instance()); SetState(CLIENT_INITIAL_STATE::Instance());
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
GetSender().Run(); GetSender().Run();
try try
+42
View File
@@ -68,6 +68,20 @@ NetPacketInit::Init()
m_data.test = htonl(0); m_data.test = htonl(0);
} }
boost::shared_ptr<NetPacket>
NetPacketInit::Clone() const
{
boost::shared_ptr<NetPacket> newPacket(new NetPacketInit);
try
{
newPacket->SetData(GetData());
} catch (const NetException &)
{
// Need to return the new packet anyway.
}
return newPacket;
}
const NetPacketHeader * const NetPacketHeader *
NetPacketInit::GetData() const NetPacketInit::GetData() const
{ {
@@ -118,6 +132,20 @@ NetPacketInitAck::Init()
m_data.test = htonl(0); m_data.test = htonl(0);
} }
boost::shared_ptr<NetPacket>
NetPacketInitAck::Clone() const
{
boost::shared_ptr<NetPacket> newPacket(new NetPacketInitAck);
try
{
newPacket->SetData(GetData());
} catch (const NetException &)
{
// Need to return the new packet anyway.
}
return newPacket;
}
const NetPacketHeader * const NetPacketHeader *
NetPacketInitAck::GetData() const NetPacketInitAck::GetData() const
{ {
@@ -168,6 +196,20 @@ NetPacketGameStart::Init()
m_data.test = htonl(0); m_data.test = htonl(0);
} }
boost::shared_ptr<NetPacket>
NetPacketGameStart::Clone() const
{
boost::shared_ptr<NetPacket> newPacket(new NetPacketGameStart);
try
{
newPacket->SetData(GetData());
} catch (const NetException &)
{
// Need to return the new packet anyway.
}
return newPacket;
}
const NetPacketHeader * const NetPacketHeader *
NetPacketGameStart::GetData() const NetPacketGameStart::GetData() const
{ {
+3 -2
View File
@@ -24,7 +24,6 @@
using namespace std; using namespace std;
#define SEND_TIMEOUT_MSEC 50
SenderThread::SenderThread(SenderCallback &cb) SenderThread::SenderThread(SenderCallback &cb)
: m_curSocket(INVALID_SOCKET), m_tmpOutBufSize(0), m_callback(cb) : m_curSocket(INVALID_SOCKET), m_tmpOutBufSize(0), m_callback(cb)
@@ -41,7 +40,9 @@ SenderThread::Send(boost::shared_ptr<NetPacket> packet, SOCKET sock)
if (packet.get() && IS_VALID_SOCKET(sock)) if (packet.get() && IS_VALID_SOCKET(sock))
{ {
boost::mutex::scoped_lock lock(m_outBufMutex); boost::mutex::scoped_lock lock(m_outBufMutex);
m_outBuf.push_back(std::make_pair(packet, sock)); if (m_outBuf.size() < SEND_QUEUE_SIZE) // Queue is limited in size.
m_outBuf.push_back(std::make_pair(packet, sock));
// TODO: Throw exception if failed.
} }
} }
+3 -2
View File
@@ -121,9 +121,10 @@ ServerRecvStateStartGame::Process(ServerRecvThread &server)
{ {
boost::shared_ptr<NetPacket> answer(new NetPacketGameStart); boost::shared_ptr<NetPacket> answer(new NetPacketGameStart);
server.SendToAllClients(answer); server.SendToAllPlayers(answer);
Thread::Msleep(100);
return MSG_SOCK_INIT_DONE; return MSG_SOCK_INTERNAL_PENDING;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
+74 -37
View File
@@ -44,10 +44,14 @@ private:
ServerRecvThread::ServerRecvThread() ServerRecvThread::ServerRecvThread()
{ {
m_senderCallback.reset(new ServerSenderCallback(*this)); m_senderCallback.reset(new ServerSenderCallback(*this));
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
} }
ServerRecvThread::~ServerRecvThread() ServerRecvThread::~ServerRecvThread()
{ {
CleanupConnectQueue();
CleanupSessionMap();
} }
void void
@@ -58,16 +62,18 @@ ServerRecvThread::StartGame()
} }
void void
ServerRecvThread::SendToAllClients(boost::shared_ptr<NetPacket> packet) ServerRecvThread::SendToAllPlayers(boost::shared_ptr<NetPacket> packet)
{ {
// TODO: possible race condition if used in multithreading // This function needs to be thread safe.
SocketSessionMap::iterator i = m_sessions.begin(); boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator end = m_sessions.end();
SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end) while (i != end)
{ {
// TODO: desparately need clone here, this is very dangerous. // Send each client a copy of the packet.
GetSender().Send(packet, i->first); GetSender().Send(boost::shared_ptr<NetPacket>(packet->Clone()), i->first);
++i; ++i;
} }
} }
@@ -83,9 +89,6 @@ void
ServerRecvThread::Main() ServerRecvThread::Main()
{ {
SetState(SERVER_INITIAL_STATE::Instance()); SetState(SERVER_INITIAL_STATE::Instance());
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
GetSender().Run(); GetSender().Run();
try try
@@ -114,7 +117,8 @@ ServerRecvThread::Main()
GetSender().SignalTermination(); GetSender().SignalTermination();
GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT); GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT);
// TODO: clear connection queue CleanupConnectQueue();
CleanupSessionMap();
} }
SOCKET SOCKET
@@ -122,31 +126,32 @@ ServerRecvThread::Select()
{ {
SOCKET retSock = INVALID_SOCKET; 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 Msleep(RECV_TIMEOUT_MSEC); // just sleep if there is no session
} }
else else
{ {
// wait for data // 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; struct timeval timeout;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = RECV_TIMEOUT_MSEC * 1000; timeout.tv_usec = RECV_TIMEOUT_MSEC * 1000;
@@ -158,8 +163,9 @@ ServerRecvThread::Select()
if (selectResult > 0) // one (or more) of the sockets is readable if (selectResult > 0) // one (or more) of the sockets is readable
{ {
// Check which socket is readable, return the first. // Check which socket is readable, return the first.
SocketSessionMap::iterator i = m_sessions.begin(); boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator end = m_sessions.end(); SocketSessionMap::iterator i = m_sessionMap.begin();
SocketSessionMap::iterator end = m_sessionMap.end();
while (i != end) while (i != end)
{ {
@@ -176,6 +182,34 @@ ServerRecvThread::Select()
return retSock; 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 & ServerRecvState &
ServerRecvThread::GetState() ServerRecvThread::GetState()
{ {
@@ -193,9 +227,10 @@ boost::shared_ptr<SessionData>
ServerRecvThread::GetSession(SOCKET sock) ServerRecvThread::GetSession(SOCKET sock)
{ {
boost::shared_ptr<SessionData> tmpSession; boost::shared_ptr<SessionData> tmpSession;
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SocketSessionMap::iterator pos = m_sessions.find(sock);
if (pos != m_sessions.end()) SocketSessionMap::iterator pos = m_sessionMap.find(sock);
if (pos != m_sessionMap.end())
{ {
tmpSession = pos->second; tmpSession = pos->second;
} }
@@ -205,15 +240,17 @@ ServerRecvThread::GetSession(SOCKET sock)
void void
ServerRecvThread::AddSession(boost::shared_ptr<ConnectData> connData, boost::shared_ptr<SessionData> sessionData) 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 // If pos points to a pair whose key is equivalent to the socket, this handle
// already exists within the list. // 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); 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 & SenderThread &
+3 -1
View File
@@ -33,6 +33,7 @@ ServerThread::ServerThread(ServerCallback &cb)
: m_callback(cb) : m_callback(cb)
{ {
m_context.reset(new ServerContext); m_context.reset(new ServerContext);
m_recvThread.reset(new ServerRecvThread);
} }
ServerThread::~ServerThread() ServerThread::~ServerThread()
@@ -71,7 +72,6 @@ ServerThread::GetCallback()
void void
ServerThread::Main() ServerThread::Main()
{ {
m_recvThread.reset(new ServerRecvThread);
try try
{ {
Listen(); Listen();
@@ -86,6 +86,8 @@ ServerThread::Main()
{ {
GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode()); GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode());
} }
GetRecvThread().SignalTermination();
GetRecvThread().Join(RECEIVER_THREAD_TERMINATE_TIMEOUT);
} }
void void
+9
View File
@@ -22,6 +22,7 @@
#define _NETPACKET_H_ #define _NETPACKET_H_
#include <string> #include <string>
#include <boost/shared_ptr.hpp>
#include <net/socket_helper.h> #include <net/socket_helper.h>
#define MAX_PACKET_SIZE 256 #define MAX_PACKET_SIZE 256
@@ -75,6 +76,8 @@ class NetPacket
public: public:
virtual ~NetPacket(); virtual ~NetPacket();
virtual boost::shared_ptr<NetPacket> Clone() const = 0;
virtual void SetData(const NetPacketHeader *p) = 0; virtual void SetData(const NetPacketHeader *p) = 0;
virtual const NetPacketHeader *GetData() const = 0; virtual const NetPacketHeader *GetData() const = 0;
@@ -90,6 +93,8 @@ public:
NetPacketInit(u_int32_t value); NetPacketInit(u_int32_t value);
virtual ~NetPacketInit(); virtual ~NetPacketInit();
virtual boost::shared_ptr<NetPacket> Clone() const;
virtual const NetPacketHeader *GetData() const; virtual const NetPacketHeader *GetData() const;
virtual void SetData(const NetPacketHeader *p); virtual void SetData(const NetPacketHeader *p);
@@ -109,6 +114,8 @@ public:
NetPacketInitAck(u_int32_t value); NetPacketInitAck(u_int32_t value);
virtual ~NetPacketInitAck(); virtual ~NetPacketInitAck();
virtual boost::shared_ptr<NetPacket> Clone() const;
virtual const NetPacketHeader *GetData() const; virtual const NetPacketHeader *GetData() const;
virtual void SetData(const NetPacketHeader *p); virtual void SetData(const NetPacketHeader *p);
@@ -128,6 +135,8 @@ public:
NetPacketGameStart(u_int32_t value); NetPacketGameStart(u_int32_t value);
virtual ~NetPacketGameStart(); virtual ~NetPacketGameStart();
virtual boost::shared_ptr<NetPacket> Clone() const;
virtual const NetPacketHeader *GetData() const; virtual const NetPacketHeader *GetData() const;
virtual void SetData(const NetPacketHeader *p); virtual void SetData(const NetPacketHeader *p);
+3 -1
View File
@@ -29,7 +29,9 @@
#include <deque> #include <deque>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#define SENDER_THREAD_TERMINATE_TIMEOUT 100 #define SENDER_THREAD_TERMINATE_TIMEOUT 200
#define SEND_TIMEOUT_MSEC 50
#define SEND_QUEUE_SIZE 200
class SenderThread : public Thread class SenderThread : public Thread
{ {
+8 -2
View File
@@ -29,6 +29,8 @@
#include <net/connectdata.h> #include <net/connectdata.h>
#include <net/sessiondata.h> #include <net/sessiondata.h>
#define RECEIVER_THREAD_TERMINATE_TIMEOUT 200
class ServerRecvState; class ServerRecvState;
class SenderThread; class SenderThread;
class ReceiverHelper; class ReceiverHelper;
@@ -42,7 +44,7 @@ public:
virtual ~ServerRecvThread(); virtual ~ServerRecvThread();
void StartGame(); void StartGame();
void SendToAllClients(boost::shared_ptr<NetPacket> packet); void SendToAllPlayers(boost::shared_ptr<NetPacket> packet);
void AddConnection(boost::shared_ptr<ConnectData> data); void AddConnection(boost::shared_ptr<ConnectData> data);
protected: protected:
@@ -54,6 +56,9 @@ protected:
SOCKET Select(); SOCKET Select();
void CleanupConnectQueue();
void CleanupSessionMap();
ServerRecvState &GetState(); ServerRecvState &GetState();
void SetState(ServerRecvState &newState); void SetState(ServerRecvState &newState);
@@ -71,7 +76,8 @@ private:
mutable boost::mutex m_connectQueueMutex; mutable boost::mutex m_connectQueueMutex;
ServerRecvState *m_curState; ServerRecvState *m_curState;
SocketSessionMap m_sessions; SocketSessionMap m_sessionMap;
mutable boost::mutex m_sessionMapMutex;
std::auto_ptr<ReceiverHelper> m_receiver; std::auto_ptr<ReceiverHelper> m_receiver;
std::auto_ptr<SenderThread> m_sender; std::auto_ptr<SenderThread> m_sender;