mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-30 08:34:16 +01:00
Fix crashes resulting from deleting cref instances in one thread while
another wanted to operate on them. The root problem is that you can't dispose of a mutex while somebody's blocking on it. So now the locking mutexes live inside the cref class. When the lock owner realizes the cref needs to die, it sets a flag and it's moved to a recycled list. A thread blocking on the mutex will then get it, but checks the flag and releases it immediately if it's being recycled. (Also improve the http interface a bit.) With these changes I've run 31K (and counting) games against the relay without a crash or deadlock (using sim_real.sh.) The main problem that remains is that sometimes two games using the same cookie wind up with two crefs (and so never connect.)
This commit is contained in:
parent
0d03ed71bb
commit
8284799abe
7 changed files with 322 additions and 279 deletions
|
@ -26,6 +26,9 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "cref.h"
|
#include "cref.h"
|
||||||
#include "xwrelay.h"
|
#include "xwrelay.h"
|
||||||
|
@ -71,23 +74,36 @@ SocketsIterator::Next()
|
||||||
* CookieRef class
|
* CookieRef class
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
CookieRef::CookieRef( const char* cookie, const char* connName, CookieID id )
|
#define ASSERT_LOCKED() \
|
||||||
: m_cookie(cookie==NULL?"":cookie)
|
assert( m_locking_thread == pthread_self() )
|
||||||
, m_connName(connName)
|
|
||||||
, m_cookieID(id)
|
void
|
||||||
, m_totalSent(0)
|
CookieRef::ReInit( const char* cookie, const char* connName, CookieID id )
|
||||||
, m_curState(XWS_INITED)
|
|
||||||
, m_nextState(XWS_INITED)
|
|
||||||
, m_eventQueue()
|
|
||||||
, m_nextHostID(HOST_ID_SERVER)
|
|
||||||
, m_nPlayersTotal(0)
|
|
||||||
, m_nPlayersHere(0)
|
|
||||||
{
|
{
|
||||||
|
m_cookie = cookie==NULL?"":cookie;
|
||||||
|
m_connName = connName;
|
||||||
|
m_cookieID = id;
|
||||||
|
m_totalSent = 0;
|
||||||
|
m_curState = XWS_INITED;
|
||||||
|
m_nextState = XWS_INITED;
|
||||||
|
m_nextHostID = HOST_ID_SERVER;
|
||||||
|
m_nPlayersTotal = 0;
|
||||||
|
m_nPlayersHere = 0;
|
||||||
|
m_locking_thread = NULL;
|
||||||
|
|
||||||
RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heatbeat );
|
RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heatbeat );
|
||||||
logf( XW_LOGINFO, "creating cref for cookie %s, connName %s",
|
logf( XW_LOGINFO, "initing cref for cookie %s, connName %s",
|
||||||
m_cookie.c_str(), m_connName.c_str() );
|
m_cookie.c_str(), m_connName.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CookieRef::CookieRef( const char* cookie, const char* connName, CookieID id )
|
||||||
|
: m_locking_thread(NULL)
|
||||||
|
{
|
||||||
|
pthread_mutex_init( &m_mutex, NULL );
|
||||||
|
ReInit( cookie, connName, id );
|
||||||
|
}
|
||||||
|
|
||||||
CookieRef::~CookieRef()
|
CookieRef::~CookieRef()
|
||||||
{
|
{
|
||||||
cancelAllConnectedTimer();
|
cancelAllConnectedTimer();
|
||||||
|
@ -95,6 +111,7 @@ CookieRef::~CookieRef()
|
||||||
/* get rid of any sockets still contained */
|
/* get rid of any sockets still contained */
|
||||||
XWThreadPool* tPool = XWThreadPool::GetTPool();
|
XWThreadPool* tPool = XWThreadPool::GetTPool();
|
||||||
|
|
||||||
|
ASSERT_LOCKED();
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
|
|
||||||
|
@ -107,10 +124,51 @@ CookieRef::~CookieRef()
|
||||||
m_sockets.erase( iter );
|
m_sockets.erase( iter );
|
||||||
}
|
}
|
||||||
|
|
||||||
logf( XW_LOGINFO, "CookieRef for %d being deleted; sent %d bytes",
|
logf( XW_LOGINFO,
|
||||||
|
"CookieRef for %d being deleted; sent %d bytes over lifetime",
|
||||||
m_cookieID, m_totalSent );
|
m_cookieID, m_totalSent );
|
||||||
} /* ~CookieRef */
|
} /* ~CookieRef */
|
||||||
|
|
||||||
|
void
|
||||||
|
CookieRef::Clear(void)
|
||||||
|
{
|
||||||
|
m_cookie = "";
|
||||||
|
m_connName = "";
|
||||||
|
m_cookieID = 0;
|
||||||
|
m_eventQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CookieRef::Lock( void )
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
pthread_mutex_lock( &m_mutex );
|
||||||
|
|
||||||
|
/* We get here possibly after having been blocked on the mutex for a
|
||||||
|
while. This cref may no longer be live. If it's not, unlock and
|
||||||
|
return. */
|
||||||
|
|
||||||
|
assert( m_locking_thread == 0 );
|
||||||
|
m_locking_thread = pthread_self();
|
||||||
|
|
||||||
|
if ( notInUse() ) {
|
||||||
|
logf( XW_LOGINFO, "%s: not locking %p because not in use", __func__, this );
|
||||||
|
success = false;
|
||||||
|
m_locking_thread = NULL;
|
||||||
|
pthread_mutex_unlock( &m_mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
} /* CookieRef::Lock */
|
||||||
|
|
||||||
|
void
|
||||||
|
CookieRef::Unlock() {
|
||||||
|
assert( m_locking_thread == pthread_self() );
|
||||||
|
m_locking_thread = NULL;
|
||||||
|
pthread_mutex_unlock( &m_mutex );
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CookieRef::_Connect( int socket, HostID hid, int nPlayersH, int nPlayersT )
|
CookieRef::_Connect( int socket, HostID hid, int nPlayersH, int nPlayersT )
|
||||||
{
|
{
|
||||||
|
@ -166,6 +224,7 @@ int
|
||||||
CookieRef::SocketForHost( HostID dest )
|
CookieRef::SocketForHost( HostID dest )
|
||||||
{
|
{
|
||||||
int socket;
|
int socket;
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.find( dest );
|
map<HostID,HostRec>::iterator iter = m_sockets.find( dest );
|
||||||
if ( iter == m_sockets.end() ) {
|
if ( iter == m_sockets.end() ) {
|
||||||
socket = -1;
|
socket = -1;
|
||||||
|
@ -235,6 +294,7 @@ CookieRef::removeSocket( int socket )
|
||||||
int count;
|
int count;
|
||||||
{
|
{
|
||||||
/* RWWriteLock rwl( &m_sockets_rwlock ); */
|
/* RWWriteLock rwl( &m_sockets_rwlock ); */
|
||||||
|
ASSERT_LOCKED();
|
||||||
|
|
||||||
count = m_sockets.size();
|
count = m_sockets.size();
|
||||||
assert( count > 0 );
|
assert( count > 0 );
|
||||||
|
@ -256,9 +316,21 @@ CookieRef::removeSocket( int socket )
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CookieRef::HasSocket( int socket )
|
CookieRef::HasSocket( int socket )
|
||||||
|
{
|
||||||
|
bool result = Lock();
|
||||||
|
if ( result ) {
|
||||||
|
result = HasSocket_locked( socket );
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CookieRef::HasSocket_locked( int socket )
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
if ( iter->second.m_socket == socket ) {
|
if ( iter->second.m_socket == socket ) {
|
||||||
|
@ -267,8 +339,9 @@ CookieRef::HasSocket( int socket )
|
||||||
}
|
}
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
} /* HasSocket */
|
} /* HasSocket_locked */
|
||||||
|
|
||||||
#ifdef RELAY_HEARTBEAT
|
#ifdef RELAY_HEARTBEAT
|
||||||
void
|
void
|
||||||
|
@ -283,7 +356,7 @@ void
|
||||||
CookieRef::_CheckHeartbeats( time_t now )
|
CookieRef::_CheckHeartbeats( time_t now )
|
||||||
{
|
{
|
||||||
logf( XW_LOGINFO, "CookieRef::_CheckHeartbeats" );
|
logf( XW_LOGINFO, "CookieRef::_CheckHeartbeats" );
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
time_t last = iter->second.m_lastHeartbeat;
|
time_t last = iter->second.m_lastHeartbeat;
|
||||||
|
@ -505,26 +578,17 @@ CookieRef::send_with_length( int socket, unsigned char* buf, int bufLen,
|
||||||
bool cascade )
|
bool cascade )
|
||||||
{
|
{
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
{
|
if ( send_with_length_unsafe( socket, buf, bufLen ) ) {
|
||||||
SocketWriteLock slock( socket );
|
RecordSent( bufLen, socket );
|
||||||
if ( slock.socketFound() ) {
|
} else {
|
||||||
if ( send_with_length_unsafe( socket, buf, bufLen ) ) {
|
failed = true;
|
||||||
RecordSent( bufLen, socket );
|
|
||||||
} else {
|
|
||||||
failed = true;
|
|
||||||
/* ok that the slock above is still in scope */
|
|
||||||
/* Can't call killSocket. It will deadlock (try to lock a mutex
|
|
||||||
we already hold) if what we're sending about is that we're
|
|
||||||
killing a socket */
|
|
||||||
/* killSocket( socket, "couldn't send" ); */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( failed && cascade ) {
|
if ( failed && cascade ) {
|
||||||
_Remove( socket );
|
_Remove( socket );
|
||||||
XWThreadPool::GetTPool()->CloseSocket( socket );
|
XWThreadPool::GetTPool()->CloseSocket( socket );
|
||||||
}
|
}
|
||||||
}
|
} /* send_with_length */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
putNetShort( unsigned char** bufpp, unsigned short s )
|
putNetShort( unsigned char** bufpp, unsigned short s )
|
||||||
|
@ -566,6 +630,7 @@ void
|
||||||
CookieRef::reducePlayerCounts( int socket )
|
CookieRef::reducePlayerCounts( int socket )
|
||||||
{
|
{
|
||||||
logf( XW_LOGVERBOSE1, "reducePlayerCounts on socket %d", socket );
|
logf( XW_LOGVERBOSE1, "reducePlayerCounts on socket %d", socket );
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
|
|
||||||
|
@ -646,6 +711,7 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
||||||
assert( id != HOST_ID_NONE );
|
assert( id != HOST_ID_NONE );
|
||||||
logf( XW_LOGINFO, "remembering pair: hostid=%x, socket=%d", id, socket );
|
logf( XW_LOGINFO, "remembering pair: hostid=%x, socket=%d", id, socket );
|
||||||
HostRec hr(socket, nPlayersH, nPlayersT);
|
HostRec hr(socket, nPlayersH, nPlayersT);
|
||||||
|
ASSERT_LOCKED();
|
||||||
m_sockets.insert( pair<HostID,HostRec>(id,hr) );
|
m_sockets.insert( pair<HostID,HostRec>(id,hr) );
|
||||||
|
|
||||||
/* Now send the response */
|
/* Now send the response */
|
||||||
|
@ -722,7 +788,7 @@ CookieRef::notifyOthers( int socket, XWRelayMsg msg, XWREASON why )
|
||||||
assert( socket != 0 );
|
assert( socket != 0 );
|
||||||
|
|
||||||
/* RWReadLock ml( &m_sockets_rwlock ); */
|
/* RWReadLock ml( &m_sockets_rwlock ); */
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
int other = iter->second.m_socket;
|
int other = iter->second.m_socket;
|
||||||
|
@ -750,7 +816,7 @@ CookieRef::sendAllHere( bool includeName )
|
||||||
memcpy( bufp, connName, len );
|
memcpy( bufp, connName, len );
|
||||||
bufp += len;
|
bufp += len;
|
||||||
}
|
}
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
send_with_length( iter->second.m_socket, buf, bufp-buf,
|
send_with_length( iter->second.m_socket, buf, bufp-buf,
|
||||||
|
@ -764,6 +830,7 @@ CookieRef::disconnectSockets( int socket, XWREASON why )
|
||||||
{
|
{
|
||||||
if ( socket == 0 ) {
|
if ( socket == 0 ) {
|
||||||
/* RWReadLock ml( &m_sockets_rwlock ); */
|
/* RWReadLock ml( &m_sockets_rwlock ); */
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
||||||
while ( iter != m_sockets.end() ) {
|
while ( iter != m_sockets.end() ) {
|
||||||
assert( iter->second.m_socket != 0 );
|
assert( iter->second.m_socket != 0 );
|
||||||
|
@ -783,7 +850,7 @@ CookieRef::noteHeartbeat( const CRefEvent* evt )
|
||||||
HostID id = evt->u.heart.id;
|
HostID id = evt->u.heart.id;
|
||||||
|
|
||||||
/* RWWriteLock rwl( &m_sockets_rwlock ); */
|
/* RWWriteLock rwl( &m_sockets_rwlock ); */
|
||||||
|
ASSERT_LOCKED();
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.find(id);
|
map<HostID,HostRec>::iterator iter = m_sockets.find(id);
|
||||||
if ( iter == m_sockets.end() ) {
|
if ( iter == m_sockets.end() ) {
|
||||||
logf( XW_LOGERROR, "no socket for HostID %x", id );
|
logf( XW_LOGERROR, "no socket for HostID %x", id );
|
||||||
|
@ -792,6 +859,7 @@ CookieRef::noteHeartbeat( const CRefEvent* evt )
|
||||||
/* PENDING If the message came on an unexpected socket, kill the
|
/* PENDING If the message came on an unexpected socket, kill the
|
||||||
connection. An attack is the most likely explanation. */
|
connection. An attack is the most likely explanation. */
|
||||||
assert( iter->second.m_socket == socket );
|
assert( iter->second.m_socket == socket );
|
||||||
|
/* if see this again recover from it */
|
||||||
|
|
||||||
logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d",
|
logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d",
|
||||||
iter->second.m_lastHeartbeat, uptime() );
|
iter->second.m_lastHeartbeat, uptime() );
|
||||||
|
@ -869,6 +937,7 @@ CookieRef::_PrintCookieInfo( string& out )
|
||||||
/* n messages */
|
/* n messages */
|
||||||
/* open since when */
|
/* open since when */
|
||||||
|
|
||||||
|
ASSERT_LOCKED();
|
||||||
snprintf( buf, sizeof(buf), "Hosts connected=%d; cur time = %ld\n",
|
snprintf( buf, sizeof(buf), "Hosts connected=%d; cur time = %ld\n",
|
||||||
m_sockets.size(), uptime() );
|
m_sockets.size(), uptime() );
|
||||||
out += buf;
|
out += buf;
|
||||||
|
@ -884,13 +953,29 @@ CookieRef::_PrintCookieInfo( string& out )
|
||||||
} /* PrintCookieInfo */
|
} /* PrintCookieInfo */
|
||||||
|
|
||||||
void
|
void
|
||||||
CookieRef::_FormatSockets( string& out )
|
CookieRef::_FormatHostInfo( string* hostIds, string* addrs )
|
||||||
{
|
{
|
||||||
map<HostID,HostRec>::iterator iter = m_sockets.begin();
|
logf( XW_LOGINFO, "%s", __func__ );
|
||||||
while ( iter != m_sockets.end() ) {
|
|
||||||
char buf[8];
|
ASSERT_LOCKED();
|
||||||
snprintf( buf, sizeof(buf), "%d ", iter->first );
|
map<HostID,HostRec>::iterator iter;
|
||||||
out += buf;
|
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
|
||||||
++iter;
|
|
||||||
|
if ( !!hostIds ) {
|
||||||
|
char buf[8];
|
||||||
|
snprintf( buf, sizeof(buf), "%d ", iter->first );
|
||||||
|
*hostIds += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !!addrs ) {
|
||||||
|
int s = iter->second.m_socket;
|
||||||
|
struct sockaddr_in name;
|
||||||
|
socklen_t siz = sizeof(name);
|
||||||
|
if ( 0 == getpeername( s, (struct sockaddr*)&name, &siz) ) {
|
||||||
|
char buf[32] = {0};
|
||||||
|
snprintf( buf, sizeof(buf), "%s ", inet_ntoa(name.sin_addr) );
|
||||||
|
*addrs += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,14 @@ class CookieRef {
|
||||||
friend class CookieMapIterator;
|
friend class CookieMapIterator;
|
||||||
|
|
||||||
CookieRef( const char* cookie, const char* connName, CookieID id );
|
CookieRef( const char* cookie, const char* connName, CookieID id );
|
||||||
|
void ReInit( const char* cookie, const char* connName, CookieID id );
|
||||||
~CookieRef();
|
~CookieRef();
|
||||||
|
|
||||||
|
void Clear(void); /* make clear it's unused */
|
||||||
|
|
||||||
|
bool Lock( void );
|
||||||
|
void Unlock( void );
|
||||||
|
|
||||||
/* Within this cookie, remember that this hostID and socket go together.
|
/* Within this cookie, remember that this hostID and socket go together.
|
||||||
If the hostID is HOST_ID_SERVER, it's the server. */
|
If the hostID is HOST_ID_SERVER, it's the server. */
|
||||||
CookieID GetCookieID() { return m_cookieID; }
|
CookieID GetCookieID() { return m_cookieID; }
|
||||||
|
@ -73,6 +79,7 @@ class CookieRef {
|
||||||
bool HostKnown( HostID host ) { return -1 != SocketForHost( host ); }
|
bool HostKnown( HostID host ) { return -1 != SocketForHost( host ); }
|
||||||
int CountSockets() { return m_sockets.size(); }
|
int CountSockets() { return m_sockets.size(); }
|
||||||
bool HasSocket( int socket );
|
bool HasSocket( int socket );
|
||||||
|
bool HasSocket_locked( int socket );
|
||||||
const char* Cookie() { return m_cookie.c_str(); }
|
const char* Cookie() { return m_cookie.c_str(); }
|
||||||
const char* ConnName() { return m_connName.c_str(); }
|
const char* ConnName() { return m_connName.c_str(); }
|
||||||
|
|
||||||
|
@ -85,7 +92,7 @@ class CookieRef {
|
||||||
/* for console */
|
/* for console */
|
||||||
void _PrintCookieInfo( string& out );
|
void _PrintCookieInfo( string& out );
|
||||||
void PrintSocketInfo( string& out, int socket );
|
void PrintSocketInfo( string& out, int socket );
|
||||||
void _FormatSockets( string& out );
|
void _FormatHostInfo( string* hostIds, string* addrs );
|
||||||
|
|
||||||
static CookieMapIterator GetCookieIterator();
|
static CookieMapIterator GetCookieIterator();
|
||||||
|
|
||||||
|
@ -188,6 +195,9 @@ class CookieRef {
|
||||||
void sendAllHere( bool includeName );
|
void sendAllHere( bool includeName );
|
||||||
|
|
||||||
HostID nextHostID() { return ++m_nextHostID; }
|
HostID nextHostID() { return ++m_nextHostID; }
|
||||||
|
|
||||||
|
bool notInUse(void) { return m_cookieID == 0; }
|
||||||
|
|
||||||
/* timer callback */
|
/* timer callback */
|
||||||
static void s_checkAllConnected( void* closure );
|
static void s_checkAllConnected( void* closure );
|
||||||
|
|
||||||
|
@ -213,6 +223,10 @@ class CookieRef {
|
||||||
HostID m_nextHostID;
|
HostID m_nextHostID;
|
||||||
int m_nPlayersTotal;
|
int m_nPlayersTotal;
|
||||||
int m_nPlayersHere;
|
int m_nPlayersHere;
|
||||||
|
|
||||||
|
pthread_mutex_t m_mutex;
|
||||||
|
|
||||||
|
pthread_t m_locking_thread; /* for debugging only */
|
||||||
}; /* CookieRef */
|
}; /* CookieRef */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -58,8 +58,8 @@ CRefMgr::CRefMgr()
|
||||||
: m_nextCID(0)
|
: m_nextCID(0)
|
||||||
{
|
{
|
||||||
/* should be using pthread_once() here */
|
/* should be using pthread_once() here */
|
||||||
pthread_mutex_init( &m_guard, NULL );
|
|
||||||
pthread_mutex_init( &m_SocketStuffMutex, NULL );
|
pthread_mutex_init( &m_SocketStuffMutex, NULL );
|
||||||
|
pthread_mutex_init( &m_freeList_mutex, NULL );
|
||||||
pthread_rwlock_init( &m_cookieMapRWLock, NULL );
|
pthread_rwlock_init( &m_cookieMapRWLock, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ CRefMgr::~CRefMgr()
|
||||||
{
|
{
|
||||||
assert( this == s_instance );
|
assert( this == s_instance );
|
||||||
|
|
||||||
pthread_mutex_destroy( &m_guard );
|
pthread_mutex_destroy( &m_freeList_mutex );
|
||||||
pthread_rwlock_destroy( &m_cookieMapRWLock );
|
pthread_rwlock_destroy( &m_cookieMapRWLock );
|
||||||
|
|
||||||
SocketMap::iterator iter;
|
SocketMap::iterator iter;
|
||||||
|
@ -84,17 +84,21 @@ CRefMgr::CloseAll()
|
||||||
{
|
{
|
||||||
/* Get every cref instance, shut it down */
|
/* Get every cref instance, shut it down */
|
||||||
|
|
||||||
RWWriteLock rwl( &m_cookieMapRWLock );
|
for ( ; ; ) {
|
||||||
CookieMap::iterator iter = m_cookieMap.begin();
|
CookieRef* cref = NULL;
|
||||||
while ( iter != m_cookieMap.end() ) {
|
|
||||||
CookieRef* cref = iter->second;
|
|
||||||
{
|
{
|
||||||
SafeCref scr( cref );
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
||||||
scr.Shutdown();
|
CookieMap::iterator iter = m_cookieMap.begin();
|
||||||
|
if ( iter == m_cookieMap.end() ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cref = iter->second;
|
||||||
|
{
|
||||||
|
SafeCref scr( cref ); /* cref */
|
||||||
|
scr.Shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++iter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* CloseAll */
|
} /* CloseAll */
|
||||||
|
|
||||||
CookieRef*
|
CookieRef*
|
||||||
|
@ -175,20 +179,32 @@ CRefMgr::cookieIDForConnName( const char* connName )
|
||||||
return cid;
|
return cid;
|
||||||
} /* cookieIDForConnName */
|
} /* cookieIDForConnName */
|
||||||
|
|
||||||
|
void
|
||||||
|
CRefMgr::addToFreeList( CookieRef* cref )
|
||||||
|
{
|
||||||
|
MutexLock ml( &m_freeList_mutex );
|
||||||
|
m_freeList.push_back( cref );
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieRef*
|
||||||
|
CRefMgr::getFromFreeList( void )
|
||||||
|
{
|
||||||
|
CookieRef* cref = NULL;
|
||||||
|
MutexLock ml( &m_freeList_mutex );
|
||||||
|
if ( m_freeList.size() > 0 ) {
|
||||||
|
cref = m_freeList.front();
|
||||||
|
m_freeList.pop_front();
|
||||||
|
}
|
||||||
|
return cref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CookieRef*
|
CookieRef*
|
||||||
CRefMgr::getMakeCookieRef_locked( const char* cORn, bool isCookie, HostID hid,
|
CRefMgr::getMakeCookieRef_locked( const char* cORn, bool isCookie, HostID hid,
|
||||||
int socket, int nPlayersH, int nPlayersT )
|
int socket, int nPlayersH, int nPlayersT )
|
||||||
{
|
{
|
||||||
CookieRef* cref;
|
CookieRef* cref;
|
||||||
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "tlm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_lock( &m_guard );
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "slm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We have a cookie from a new connection. This may be the first time
|
/* We have a cookie from a new connection. This may be the first time
|
||||||
it's been seen, or there may be a game currently in the
|
it's been seen, or there may be a game currently in the
|
||||||
XW_ST_CONNECTING state, or it may be a dupe of a connect packet. So we
|
XW_ST_CONNECTING state, or it may be a dupe of a connect packet. So we
|
||||||
|
@ -216,13 +232,6 @@ CRefMgr::getMakeCookieRef_locked( const char* cORn, bool isCookie, HostID hid,
|
||||||
cref = AddNew( cookie, connName, cid );
|
cref = AddNew( cookie, connName, cid );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( cref == NULL ) {
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( &m_guard );
|
|
||||||
}
|
|
||||||
|
|
||||||
return cref;
|
return cref;
|
||||||
} /* getMakeCookieRef_locked */
|
} /* getMakeCookieRef_locked */
|
||||||
|
|
||||||
|
@ -254,7 +263,7 @@ CRefMgr::Disassociate( int socket, CookieRef* cref )
|
||||||
MutexLock ml( &m_SocketStuffMutex );
|
MutexLock ml( &m_SocketStuffMutex );
|
||||||
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
||||||
if ( iter == m_SocketStuff.end() ) {
|
if ( iter == m_SocketStuff.end() ) {
|
||||||
logf( XW_LOGERROR, "can't find cref/threadID pair for socket %d", socket );
|
logf( XW_LOGERROR, "can't find SocketStuff for socket %d", socket );
|
||||||
} else {
|
} else {
|
||||||
SocketStuff* stuff = iter->second;
|
SocketStuff* stuff = iter->second;
|
||||||
assert( cref == NULL || stuff->m_cref == cref );
|
assert( cref == NULL || stuff->m_cref == cref );
|
||||||
|
@ -263,24 +272,33 @@ CRefMgr::Disassociate( int socket, CookieRef* cref )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
pthread_mutex_t*
|
pthread_mutex_t*
|
||||||
CRefMgr::GetWriteMutexForSocket( int socket )
|
CRefMgr::GetWriteMutexForSocket( int socket )
|
||||||
{
|
{
|
||||||
|
pthread_mutex_t* mutex = NULL;
|
||||||
MutexLock ml( &m_SocketStuffMutex );
|
MutexLock ml( &m_SocketStuffMutex );
|
||||||
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
||||||
if ( iter != m_SocketStuff.end() ) {
|
if ( iter != m_SocketStuff.end() ) {
|
||||||
SocketStuff* stuff = iter->second;
|
SocketStuff* stuff = iter->second;
|
||||||
return &stuff->m_writeMutex;
|
/* this is dangerous! What if we want to nuke SocketStuff while this
|
||||||
|
is locked? And shouldn't it be the case that only one thread at a
|
||||||
|
time can be trying to write to one of a given cref's sockets since
|
||||||
|
only one thread at a time is handling a cref? */
|
||||||
|
mutex = &stuff->m_writeMutex;
|
||||||
}
|
}
|
||||||
logf( XW_LOGERROR, "GetWriteMutexForSocket: not found" );
|
logf( XW_LOGERROR, "GetWriteMutexForSocket: not found" );
|
||||||
return NULL;
|
return mutex;
|
||||||
} /* GetWriteMutexForSocket */
|
} /* GetWriteMutexForSocket */
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
CRefMgr::RemoveSocketRefs( int socket )
|
CRefMgr::RemoveSocketRefs( int socket )
|
||||||
{
|
{
|
||||||
SafeCref scr( socket );
|
{
|
||||||
scr.Remove( socket );
|
SafeCref scr( socket );
|
||||||
|
scr.Remove( socket );
|
||||||
|
}
|
||||||
|
|
||||||
Disassociate( socket, NULL );
|
Disassociate( socket, NULL );
|
||||||
}
|
}
|
||||||
|
@ -311,123 +329,24 @@ CRefMgr::MakeSocketsIterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieRef*
|
CookieRef*
|
||||||
CRefMgr::getCookieRef_locked( CookieID cookieID )
|
CRefMgr::getCookieRef( CookieID cookieID )
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_LOCKS
|
return getCookieRef_impl( cookieID );
|
||||||
logf( XW_LOGINFO, "tlm %p", &m_guard );
|
} /* getCookieRef */
|
||||||
#endif
|
|
||||||
pthread_mutex_lock( &m_guard );
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "slm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CookieRef* cref = getCookieRef_impl( cookieID );
|
|
||||||
|
|
||||||
if ( cref == NULL ) {
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( &m_guard );
|
|
||||||
}
|
|
||||||
|
|
||||||
return cref;
|
|
||||||
} /* getCookieRef_locked */
|
|
||||||
|
|
||||||
CookieRef*
|
CookieRef*
|
||||||
CRefMgr::getCookieRef_locked( int socket )
|
CRefMgr::getCookieRef( int socket )
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "tlm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_lock( &m_guard );
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "slm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
CookieRef* cref = NULL;
|
CookieRef* cref = NULL;
|
||||||
|
MutexLock ml( &m_SocketStuffMutex );
|
||||||
CookieMap::iterator iter = m_cookieMap.begin();
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
||||||
while ( iter != m_cookieMap.end() ) {
|
if ( iter != m_SocketStuff.end() ) {
|
||||||
CookieRef* tmp = iter->second;
|
SocketStuff* stuff = iter->second;
|
||||||
if ( tmp->HasSocket( socket ) ) {
|
cref = stuff->m_cref;
|
||||||
cref = tmp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( cref == NULL ) {
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( &m_guard );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cref;
|
return cref;
|
||||||
} /* getCookieRef_locked */
|
} /* getCookieRef */
|
||||||
|
|
||||||
bool
|
|
||||||
CRefMgr::checkCookieRef_locked( CookieRef* cref )
|
|
||||||
{
|
|
||||||
bool exists = true;
|
|
||||||
assert( cref != NULL );
|
|
||||||
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "tlm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_lock( &m_guard );
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "slm %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pthread_mutex_t* cref_mutex = m_crefMutexes[cref];
|
|
||||||
logf( XW_LOGINFO, "checkCookieRef_locked: cref_mutex=%p", cref_mutex );
|
|
||||||
|
|
||||||
if ( cref_mutex == NULL ) {
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( &m_guard );
|
|
||||||
exists = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exists;
|
|
||||||
} /* checkCookieRef_locked */
|
|
||||||
|
|
||||||
bool
|
|
||||||
CRefMgr::LockCref( CookieRef* cref )
|
|
||||||
{
|
|
||||||
/* assertion: m_guard is locked */
|
|
||||||
|
|
||||||
pthread_mutex_t* cref_mutex = m_crefMutexes[cref];
|
|
||||||
if ( cref_mutex == NULL ) {
|
|
||||||
cref_mutex = (pthread_mutex_t*)malloc( sizeof( *cref_mutex ) );
|
|
||||||
pthread_mutex_init( cref_mutex, NULL );
|
|
||||||
m_crefMutexes[cref] = cref_mutex;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "tlm %p", cref_mutex );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_lock( cref_mutex );
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "slm %p", cref_mutex );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", &m_guard );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( &m_guard );
|
|
||||||
return true;
|
|
||||||
} /* LockCref */
|
|
||||||
|
|
||||||
void
|
|
||||||
CRefMgr::UnlockCref( CookieRef* cref )
|
|
||||||
{
|
|
||||||
pthread_mutex_t* cref_mutex = m_crefMutexes[cref];
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", cref_mutex );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( cref_mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef RELAY_HEARTBEAT
|
#ifdef RELAY_HEARTBEAT
|
||||||
/* static */ void
|
/* static */ void
|
||||||
|
@ -444,11 +363,21 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id )
|
||||||
logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, id=%d", __func__,
|
logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, id=%d", __func__,
|
||||||
cookie, connName, id );
|
cookie, connName, id );
|
||||||
CookieRef* exists = getCookieRef_impl( id );
|
CookieRef* exists = getCookieRef_impl( id );
|
||||||
assert( exists == NULL );
|
assert( exists == NULL ); /* failed once */
|
||||||
|
|
||||||
|
CookieRef* ref = getFromFreeList();
|
||||||
|
|
||||||
RWWriteLock rwl( &m_cookieMapRWLock );
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
||||||
logf( XW_LOGINFO, "making new cref: %d", id );
|
logf( XW_LOGINFO, "making new cref: %d", id );
|
||||||
CookieRef* ref = new CookieRef( cookie, connName, id );
|
|
||||||
|
if ( !!ref ) {
|
||||||
|
logf( XW_LOGINFO, "using from free list" );
|
||||||
|
ref->ReInit( cookie, connName, id );
|
||||||
|
} else {
|
||||||
|
logf( XW_LOGINFO, "calling constructor" );
|
||||||
|
ref = new CookieRef( cookie, connName, id );
|
||||||
|
}
|
||||||
|
|
||||||
m_cookieMap.insert( pair<CookieID, CookieRef*>(ref->GetCookieID(), ref ) );
|
m_cookieMap.insert( pair<CookieID, CookieRef*>(ref->GetCookieID(), ref ) );
|
||||||
logf( XW_LOGINFO, "paired cookie %s/connName %s with id %d",
|
logf( XW_LOGINFO, "paired cookie %s/connName %s with id %d",
|
||||||
(cookie?cookie:"NULL"), connName, ref->GetCookieID() );
|
(cookie?cookie:"NULL"), connName, ref->GetCookieID() );
|
||||||
|
@ -467,59 +396,52 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id )
|
||||||
} /* AddNew */
|
} /* AddNew */
|
||||||
|
|
||||||
void
|
void
|
||||||
CRefMgr::Delete( CookieRef* cref )
|
CRefMgr::Recycle( CookieRef* cref )
|
||||||
{
|
{
|
||||||
|
logf( XW_LOGINFO, "%s(cref=%p,cookie=%s)", __func__, cref, cref->Cookie() );
|
||||||
|
CookieID id = cref->GetCookieID();
|
||||||
|
cref->Clear();
|
||||||
|
addToFreeList( cref );
|
||||||
|
|
||||||
|
cref->Unlock();
|
||||||
|
|
||||||
|
/* don't grab this lock until after releasing cref's lock; otherwise
|
||||||
|
deadlock happens. */
|
||||||
RWWriteLock rwl( &m_cookieMapRWLock );
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
||||||
|
|
||||||
CookieMap::iterator iter = m_cookieMap.begin();
|
CookieMap::iterator iter = m_cookieMap.begin();
|
||||||
while ( iter != m_cookieMap.end() ) {
|
while ( iter != m_cookieMap.end() ) {
|
||||||
CookieRef* ref = iter->second;
|
CookieRef* ref = iter->second;
|
||||||
if ( ref == cref ) {
|
if ( ref == cref ) {
|
||||||
logf( XW_LOGINFO, "erasing cref" );
|
logf( XW_LOGINFO, "%s: erasing cref cid %d", __func__, id );
|
||||||
m_cookieMap.erase( iter );
|
m_cookieMap.erase( iter );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_t* cref_mutex = m_crefMutexes[cref];
|
|
||||||
#ifdef DEBUG_LOCKS
|
|
||||||
logf( XW_LOGINFO, "ULM %p", cref_mutex );
|
|
||||||
#endif
|
|
||||||
pthread_mutex_unlock( cref_mutex );
|
|
||||||
pthread_mutex_destroy( cref_mutex );
|
|
||||||
free( cref_mutex );
|
|
||||||
|
|
||||||
map<CookieRef*,pthread_mutex_t*>::iterator iter2;
|
|
||||||
iter2 = m_crefMutexes.find(cref);
|
|
||||||
assert( iter2 != m_crefMutexes.end() );
|
|
||||||
m_crefMutexes.erase( iter2 );
|
|
||||||
|
|
||||||
delete cref;
|
|
||||||
|
|
||||||
#ifdef RELAY_HEARTBEAT
|
#ifdef RELAY_HEARTBEAT
|
||||||
if ( m_cookieMap.size() == 0 ) {
|
if ( m_cookieMap.size() == 0 ) {
|
||||||
TimerMgr::GetTimerMgr()->ClearTimer( heartbeatProc, this );
|
TimerMgr::GetTimerMgr()->ClearTimer( heartbeatProc, this );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} /* CRefMgr::Recycle */
|
||||||
logf( XW_LOGINFO, "CRefMgr::Delete done" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CRefMgr::Delete( CookieID id )
|
CRefMgr::Recycle( CookieID id )
|
||||||
{
|
{
|
||||||
CookieRef* cref = getCookieRef_impl( id );
|
CookieRef* cref = getCookieRef( id );
|
||||||
if ( cref != NULL ) {
|
if ( cref != NULL ) {
|
||||||
Delete( cref );
|
Recycle( cref );
|
||||||
}
|
}
|
||||||
} /* Delete */
|
} /* Delete */
|
||||||
|
|
||||||
void
|
void
|
||||||
CRefMgr::Delete( const char* connName )
|
CRefMgr::Recycle( const char* connName )
|
||||||
{
|
{
|
||||||
CookieID id = cookieIDForConnName( connName );
|
CookieID id = cookieIDForConnName( connName );
|
||||||
Delete( id );
|
Recycle( id );
|
||||||
} /* Delete */
|
} /* Delete */
|
||||||
|
|
||||||
CookieRef*
|
CookieRef*
|
||||||
|
@ -566,13 +488,14 @@ CRefMgr::checkHeartbeats( time_t now )
|
||||||
/* static */ CookieMapIterator
|
/* static */ CookieMapIterator
|
||||||
CRefMgr::GetCookieIterator()
|
CRefMgr::GetCookieIterator()
|
||||||
{
|
{
|
||||||
CookieMapIterator iter;
|
CookieMapIterator iter(&m_cookieMapRWLock);
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CookieMapIterator::CookieMapIterator()
|
CookieMapIterator::CookieMapIterator(pthread_rwlock_t* rwlock)
|
||||||
: _iter( CRefMgr::Get()->m_cookieMap.begin() )
|
: m_rwl( rwlock )
|
||||||
|
,_iter( CRefMgr::Get()->m_cookieMap.begin() )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,62 +517,64 @@ CookieMapIterator::Next()
|
||||||
|
|
||||||
SafeCref::SafeCref( const char* cORn, bool isCookie, HostID hid, int socket,
|
SafeCref::SafeCref( const char* cORn, bool isCookie, HostID hid, int socket,
|
||||||
int nPlayersH, int nPlayersT )
|
int nPlayersH, int nPlayersT )
|
||||||
: m_cref( NULL )
|
: m_cref( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
|
, m_isValid( false )
|
||||||
{
|
{
|
||||||
CookieRef* cref;
|
CookieRef* cref;
|
||||||
|
|
||||||
cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid, socket,
|
cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid, socket,
|
||||||
nPlayersH, nPlayersT );
|
nPlayersH, nPlayersT );
|
||||||
if ( cref != NULL ) {
|
if ( cref != NULL ) {
|
||||||
if ( m_mgr->LockCref( cref ) ) {
|
cref->Lock();
|
||||||
m_cref = cref;
|
m_cref = cref;
|
||||||
}
|
m_isValid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeCref::SafeCref( CookieID connID )
|
SafeCref::SafeCref( CookieID connID, bool failOk )
|
||||||
: m_cref( NULL )
|
: m_cref( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
|
, m_isValid( false )
|
||||||
{
|
{
|
||||||
CookieRef* cref = m_mgr->getCookieRef_locked( connID );
|
CookieRef* cref = m_mgr->getCookieRef( connID );
|
||||||
if ( cref != NULL ) {
|
if ( cref != NULL ) { /* known cookie? */
|
||||||
if ( m_mgr->LockCref( cref ) ) {
|
m_locked = cref->Lock();
|
||||||
m_cref = cref;
|
m_isValid = m_locked && connID == cref->GetCookieID();
|
||||||
}
|
m_cref = cref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeCref::SafeCref( int socket )
|
SafeCref::SafeCref( int socket )
|
||||||
: m_cref( NULL )
|
: m_cref( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
|
, m_isValid( false )
|
||||||
{
|
{
|
||||||
CookieRef* cref = m_mgr->getCookieRef_locked( socket );
|
CookieRef* cref = m_mgr->getCookieRef( socket );
|
||||||
if ( cref != NULL ) {
|
if ( cref != NULL ) { /* known socket? */
|
||||||
if ( m_mgr->LockCref( cref ) ) {
|
m_locked = cref->Lock();
|
||||||
m_cref = cref;
|
m_isValid = m_locked && cref->HasSocket_locked( socket );
|
||||||
}
|
m_cref = cref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeCref::SafeCref( CookieRef* cref )
|
SafeCref::SafeCref( CookieRef* cref )
|
||||||
: m_cref( NULL )
|
: m_cref( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
|
, m_isValid( false )
|
||||||
{
|
{
|
||||||
if ( m_mgr->checkCookieRef_locked( cref ) ) {
|
m_locked = cref->Lock();
|
||||||
if ( m_mgr->LockCref( cref ) ) {
|
m_isValid = m_locked;
|
||||||
m_cref = cref;
|
m_cref = cref;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeCref::~SafeCref()
|
SafeCref::~SafeCref()
|
||||||
{
|
{
|
||||||
if ( m_cref != NULL ) {
|
if ( m_cref != NULL && m_locked ) {
|
||||||
if ( m_cref->ShouldDie() ) {
|
if ( m_cref->ShouldDie() ) {
|
||||||
m_mgr->Delete( m_cref );
|
m_mgr->Recycle( m_cref );
|
||||||
} else {
|
} else {
|
||||||
m_mgr->UnlockCref( m_cref );
|
m_cref->Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@
|
||||||
#ifndef _CREFMGR_H_
|
#ifndef _CREFMGR_H_
|
||||||
#define _CREFMGR_H_
|
#define _CREFMGR_H_
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "cref.h"
|
#include "cref.h"
|
||||||
|
#include "mlock.h"
|
||||||
|
|
||||||
typedef map<CookieID,CookieRef*> CookieMap;
|
typedef map<CookieID,CookieRef*> CookieMap;
|
||||||
class CookieMapIterator;
|
class CookieMapIterator;
|
||||||
|
@ -59,9 +62,9 @@ class CRefMgr {
|
||||||
CookieMapIterator GetCookieIterator();
|
CookieMapIterator GetCookieIterator();
|
||||||
|
|
||||||
/* PENDING. These need to go through SafeCref */
|
/* PENDING. These need to go through SafeCref */
|
||||||
void Delete( CookieID id );
|
void Recycle( CookieID id );
|
||||||
void Delete( CookieRef* cref );
|
void Recycle( CookieRef* cref );
|
||||||
void Delete( const char* connName );
|
void Recycle( const char* connName );
|
||||||
CookieID CookieIdForName( const char* name );
|
CookieID CookieIdForName( const char* name );
|
||||||
|
|
||||||
/* For use from ctrl only!!!! */
|
/* For use from ctrl only!!!! */
|
||||||
|
@ -81,11 +84,24 @@ class CRefMgr {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class SafeCref;
|
friend class SafeCref;
|
||||||
|
|
||||||
|
/* We'll recycle cref instances rather than free and new them. This
|
||||||
|
solves, inelegantly, a problem where I want to free an instance (while
|
||||||
|
holding its mutex) but can't know if other threads are trying to obtain
|
||||||
|
that mutex. It's illegal to destroy a mutex somebody's trying to lock.
|
||||||
|
So we recycle, let the other thread succeed in locking but then quickly
|
||||||
|
discover that the cref it got isn't what it wants. See the SafeCref
|
||||||
|
class. */
|
||||||
|
list<CookieRef*> m_freeList;
|
||||||
|
pthread_mutex_t m_freeList_mutex;
|
||||||
|
void addToFreeList( CookieRef* cref );
|
||||||
|
CookieRef* getFromFreeList( void );
|
||||||
|
|
||||||
CookieRef* getMakeCookieRef_locked( const char* cORn, bool isCookie,
|
CookieRef* getMakeCookieRef_locked( const char* cORn, bool isCookie,
|
||||||
HostID hid, int socket,
|
HostID hid, int socket,
|
||||||
int nPlayersH, int nPlayersT );
|
int nPlayersH, int nPlayersT );
|
||||||
CookieRef* getCookieRef_locked( CookieID cookieID );
|
CookieRef* getCookieRef( CookieID cookieID );
|
||||||
CookieRef* getCookieRef_locked( int socket );
|
CookieRef* getCookieRef( int socket );
|
||||||
bool checkCookieRef_locked( CookieRef* cref );
|
bool checkCookieRef_locked( CookieRef* cref );
|
||||||
CookieRef* getCookieRef_impl( CookieID cookieID );
|
CookieRef* getCookieRef_impl( CookieID cookieID );
|
||||||
CookieRef* AddNew( const char* cookie, const char* connName, CookieID id );
|
CookieRef* AddNew( const char* cookie, const char* connName, CookieID id );
|
||||||
|
@ -100,12 +116,9 @@ class CRefMgr {
|
||||||
|
|
||||||
CookieID m_nextCID;
|
CookieID m_nextCID;
|
||||||
|
|
||||||
bool LockCref( CookieRef* cref );
|
void LockCref( CookieRef* cref );
|
||||||
void UnlockCref( CookieRef* cref );
|
void UnlockCref( CookieRef* cref );
|
||||||
|
|
||||||
pthread_mutex_t m_guard;
|
|
||||||
map<CookieRef*,pthread_mutex_t*> m_crefMutexes;
|
|
||||||
|
|
||||||
pthread_rwlock_t m_cookieMapRWLock;
|
pthread_rwlock_t m_cookieMapRWLock;
|
||||||
CookieMap m_cookieMap;
|
CookieMap m_cookieMap;
|
||||||
|
|
||||||
|
@ -124,7 +137,7 @@ class SafeCref {
|
||||||
public:
|
public:
|
||||||
SafeCref( const char* cookieOrConnName, bool cookie, HostID hid,
|
SafeCref( const char* cookieOrConnName, bool cookie, HostID hid,
|
||||||
int socket, int nPlayersH, int nPlayersT );
|
int socket, int nPlayersH, int nPlayersT );
|
||||||
SafeCref( CookieID cid );
|
SafeCref( CookieID cid, bool failOk = false );
|
||||||
SafeCref( int socket );
|
SafeCref( int socket );
|
||||||
SafeCref( CookieRef* cref );
|
SafeCref( CookieRef* cref );
|
||||||
~SafeCref();
|
~SafeCref();
|
||||||
|
@ -249,29 +262,29 @@ class SafeCref {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* GetHostsConnected( string& str ) {
|
void GetHostsConnected( string* hosts, string* addrs ) {
|
||||||
if ( IsValid() ) {
|
if ( IsValid() ) {
|
||||||
m_cref->_FormatSockets( str );
|
m_cref->_FormatHostInfo( hosts, addrs );
|
||||||
return str.c_str();
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsValid() { return m_isValid; }
|
||||||
private:
|
private:
|
||||||
bool IsValid() { return m_cref != NULL; }
|
|
||||||
|
|
||||||
CookieRef* m_cref;
|
CookieRef* m_cref;
|
||||||
CRefMgr* m_mgr;
|
CRefMgr* m_mgr;
|
||||||
|
bool m_isValid;
|
||||||
|
bool m_locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class CookieMapIterator {
|
class CookieMapIterator {
|
||||||
public:
|
public:
|
||||||
CookieMapIterator();
|
CookieMapIterator(pthread_rwlock_t* rwlock);
|
||||||
~CookieMapIterator() {}
|
~CookieMapIterator() {}
|
||||||
CookieID Next();
|
CookieID Next();
|
||||||
private:
|
private:
|
||||||
|
RWReadLock m_rwl;
|
||||||
CookieMap::const_iterator _iter;
|
CookieMap::const_iterator _iter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -195,10 +195,10 @@ cmd_kill_eject( int socket, const char** args )
|
||||||
const char* id = args[3];
|
const char* id = args[3];
|
||||||
if ( idhow != NULL && id != NULL ) {
|
if ( idhow != NULL && id != NULL ) {
|
||||||
if ( 0 == strcmp( idhow, "name" ) ) {
|
if ( 0 == strcmp( idhow, "name" ) ) {
|
||||||
CRefMgr::Get()->Delete( id );
|
CRefMgr::Get()->Recycle( id );
|
||||||
found = true;
|
found = true;
|
||||||
} else if ( 0 == strcmp( idhow, "id" ) ) {
|
} else if ( 0 == strcmp( idhow, "id" ) ) {
|
||||||
CRefMgr::Get()->Delete( atoi( id ) );
|
CRefMgr::Get()->Recycle( atoi( id ) );
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ format_uptime( char* buf, int len )
|
||||||
seconds %= 60;
|
seconds %= 60;
|
||||||
|
|
||||||
snprintf( buf, len,
|
snprintf( buf, len,
|
||||||
"uptime: %d D, %d H, %d M, %ld S",
|
"%d D, %d H, %d M, %ld S",
|
||||||
days, hours, minutes, seconds );
|
days, hours, minutes, seconds );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ cmd_uptime( int socket, const char** args )
|
||||||
} else {
|
} else {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
format_uptime( buf, sizeof(buf) );
|
format_uptime( buf, sizeof(buf) );
|
||||||
print_to_sock( socket, true, "%s", buf );
|
print_to_sock( socket, true, "uptime: %s", buf );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ printCrefs( FILE* fil )
|
||||||
"<th>Players Here</th>"
|
"<th>Players Here</th>"
|
||||||
"<th>State</th>"
|
"<th>State</th>"
|
||||||
"<th>Host IDs</th>"
|
"<th>Host IDs</th>"
|
||||||
|
"<th>Host IPs</th>"
|
||||||
);
|
);
|
||||||
fprintf( fil, "</tr>\n" );
|
fprintf( fil, "</tr>\n" );
|
||||||
|
|
||||||
|
@ -127,22 +128,28 @@ printCrefs( FILE* fil )
|
||||||
CookieMapIterator iter = cmgr->GetCookieIterator();
|
CookieMapIterator iter = cmgr->GetCookieIterator();
|
||||||
CookieID id;
|
CookieID id;
|
||||||
for ( id = iter.Next(); id != 0; id = iter.Next() ) {
|
for ( id = iter.Next(); id != 0; id = iter.Next() ) {
|
||||||
string tmp;
|
string hosts, addrs;
|
||||||
SafeCref scr( id );
|
SafeCref scr( id, true );
|
||||||
fprintf( fil, "<tr>"
|
if ( scr.IsValid() ) {
|
||||||
"<td>%s</td>" /* name */
|
scr.GetHostsConnected( &hosts, &addrs );
|
||||||
"<td>%s</td>" /* conn name */
|
|
||||||
"<td>%d</td>" /* cookie id */
|
fprintf( fil, "<tr>"
|
||||||
"<td>%d</td>" /* total sent */
|
"<td>%s</td>" /* name */
|
||||||
"<td>%d</td>" /* players */
|
"<td>%s</td>" /* conn name */
|
||||||
"<td>%d</td>" /* players here */
|
"<td>%d</td>" /* cookie id */
|
||||||
"<td>%s</td>" /* State */
|
"<td>%d</td>" /* total sent */
|
||||||
"<td>%s</td>" /* Hosts */
|
"<td>%d</td>" /* players */
|
||||||
"</tr>",
|
"<td>%d</td>" /* players here */
|
||||||
scr.Cookie(), scr.ConnName(), scr.GetCookieID(),
|
"<td>%s</td>" /* State */
|
||||||
scr.GetTotalSent(), scr.GetPlayersTotal(),scr.GetPlayersHere(),
|
"<td>%s</td>" /* Hosts */
|
||||||
scr.StateString(), scr.GetHostsConnected(tmp)
|
"<td>%s</td>" /* Ip addrs */
|
||||||
);
|
"</tr>",
|
||||||
|
scr.Cookie(), scr.ConnName(), scr.GetCookieID(),
|
||||||
|
scr.GetTotalSent(), scr.GetPlayersTotal(),
|
||||||
|
scr.GetPlayersHere(), scr.StateString(),
|
||||||
|
hosts.c_str(), addrs.c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf( fil, "</table>\n" );
|
fprintf( fil, "</table>\n" );
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
#include "xwrelay_priv.h"
|
#include "xwrelay_priv.h"
|
||||||
#include "cref.h"
|
#include "cref.h"
|
||||||
#include "crefmgr.h"
|
|
||||||
|
|
||||||
class MutexLock {
|
class MutexLock {
|
||||||
public:
|
public:
|
||||||
|
@ -52,9 +51,9 @@ class MutexLock {
|
||||||
|
|
||||||
class SocketWriteLock {
|
class SocketWriteLock {
|
||||||
public:
|
public:
|
||||||
SocketWriteLock( int socket )
|
SocketWriteLock( int socket, pthread_mutex_t* mutex )
|
||||||
: m_socket( socket )
|
: m_socket( socket )
|
||||||
, m_mutex( CRefMgr::Get()->GetWriteMutexForSocket( socket ) )
|
, m_mutex( mutex )
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_LOCKS
|
#ifdef DEBUG_LOCKS
|
||||||
logf( XW_LOGINFO, "tlm %p for socket %d", m_mutex, socket );
|
logf( XW_LOGINFO, "tlm %p for socket %d", m_mutex, socket );
|
||||||
|
|
Loading…
Add table
Reference in a new issue