use a simple array rather than a vector to store currently connected

devices.  It's simpler, searching for hid is faster, and it makes it
easier to detect when we try to add the same device twice.
This commit is contained in:
Eric House 2013-08-13 08:04:19 -07:00
parent 652a8ec905
commit c9406c36ae
2 changed files with 179 additions and 145 deletions

View file

@ -96,6 +96,7 @@ CookieRef::ReInit( const char* cookie, const char* connName, CookieID cid,
m_starttime = uptime(); m_starttime = uptime();
m_in_handleEvents = false; m_in_handleEvents = false;
m_langCode = langCode; m_langCode = langCode;
memset( &m_sockets, 0, sizeof(m_sockets) );
if ( RelayConfigs::GetConfigs()->GetValueFor( "SEND_DELAY_MILLIS", if ( RelayConfigs::GetConfigs()->GetValueFor( "SEND_DELAY_MILLIS",
&m_delayMicros ) ) { &m_delayMicros ) ) {
@ -133,12 +134,16 @@ CookieRef::~CookieRef()
vector<HostRec>::iterator iter; vector<HostRec>::iterator iter;
{ {
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
AddrInfo addr = iter->m_addr; HostRec* hr = m_sockets[ii];
if ( addr.isTCP() ) { if ( NULL != hr ) {
tPool->CloseSocket( &addr ); const AddrInfo* addr = &hr->m_addr;
if ( addr->isTCP() ) {
tPool->CloseSocket( addr );
}
delete hr;
m_sockets[ii] = NULL;
} }
m_sockets.erase( iter );
} }
} }
printSeeds(__func__); printSeeds(__func__);
@ -313,10 +318,11 @@ CookieRef::HostForSocket( const AddrInfo* addr )
HostID hid = HOST_ID_NONE; HostID hid = HOST_ID_NONE;
ASSERT_LOCKED(); ASSERT_LOCKED();
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
if ( iter->m_addr.equals( *addr ) ) { if ( !!hr && hr->m_addr.equals( *addr ) ) {
hid = iter->m_hostID; assert( hr->m_hostID == ii + 1 );
hid = hr->m_hostID;
assert( HOST_ID_NONE != hid ); assert( HOST_ID_NONE != hid );
break; break;
} }
@ -332,12 +338,10 @@ CookieRef::SocketForHost( HostID dest )
assert( dest != 0 ); /* don't use as lookup before assigned */ assert( dest != 0 ); /* don't use as lookup before assigned */
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; HostRec* hr = m_sockets[dest - 1];
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( !!hr ) {
if ( iter->m_hostID == dest ) { assert( hr->m_hostID == dest );
result = &iter->m_addr; result = &hr->m_addr;
break;
}
} }
// logf( XW_LOGVERBOSE0, "returning socket=%d for hostid=%x", socket, dest ); // logf( XW_LOGVERBOSE0, "returning socket=%d for hostid=%x", socket, dest );
@ -352,14 +356,14 @@ CookieRef::AlreadyHere( unsigned short seed, const AddrInfo* addr,
bool here = false; bool here = false;
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
here = iter->m_seed == seed; /* client already registered */ here = !!hr && hr->m_seed == seed; /* client already registered */
if ( here ) { if ( here ) {
if ( !addr->equals(iter->m_addr) ) { /* not just a dupe packet */ if ( !addr->equals(hr->m_addr) ) { /* not just a dupe packet */
logf( XW_LOGINFO, "%s: seeds match; socket assumed closed", logf( XW_LOGINFO, "%s: seeds match; socket assumed closed",
__func__ ); __func__ );
*prevHostID = iter->m_hostID; *prevHostID = hr->m_hostID;
} }
break; break;
} }
@ -378,19 +382,18 @@ CookieRef::AlreadyHere( HostID hid, unsigned short seed, const AddrInfo* addr,
bool here = false; bool here = false;
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
vector<HostRec>::iterator iter; HostRec* hr = m_sockets[hid-1];
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( !!hr ) {
if ( iter->m_hostID == hid ) { assert( hr->m_hostID == hid );
if ( seed != iter->m_seed ) { if ( seed != hr->m_seed ) {
*spotTaken = true; *spotTaken = true;
} else if ( addr->equals( iter->m_addr ) ) { } else if ( addr->equals( hr->m_addr ) ) {
here = true; /* dup packet */ here = true; /* dup packet */
} else { } else {
logf( XW_LOGINFO, "%s: hids match; nuking existing record " logf( XW_LOGINFO, "%s: hids match; nuking existing record "
"for socket b/c assumed closed", __func__ ); "for socket b/c assumed closed", __func__ );
m_sockets.erase( iter ); delete hr;
} m_sockets[hid-1] = NULL;
break;
} }
} }
@ -418,19 +421,20 @@ CookieRef::removeSocket( const AddrInfo* addr )
{ {
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); !found && iter != m_sockets.end(); for ( unsigned int ii = 0; !found && ii < VSIZE(m_sockets); ++ii ) {
++iter ) { HostRec* hr = m_sockets[ii];
if ( iter->m_addr.equals( *addr ) ) { if ( !!hr && hr->m_addr.equals( *addr ) ) {
if ( iter->m_ackPending ) { if ( hr->m_ackPending ) {
logf( XW_LOGINFO, logf( XW_LOGINFO,
"%s: Never got ack; removing hid %d from DB", "%s: Never got ack; removing hid %d from DB",
__func__, iter->m_hostID ); __func__, hr->m_hostID );
DBMgr::Get()->RmDeviceByHid( ConnName(), iter->m_hostID ); DBMgr::Get()->RmDeviceByHid( ConnName(), hr->m_hostID );
m_nPlayersHere -= iter->m_nPlayersH; m_nPlayersHere -= hr->m_nPlayersH;
cancelAckTimer( iter->m_hostID ); cancelAckTimer( hr->m_hostID );
} }
m_sockets.erase(iter); m_sockets[ii] = NULL;
delete hr;
found = true; found = true;
} }
} }
@ -441,7 +445,7 @@ CookieRef::removeSocket( const AddrInfo* addr )
printSeeds(__func__); printSeeds(__func__);
if ( m_sockets.size() == 0 ) { if ( 0 == CountSockets() ) {
pushLastSocketGoneEvent(); pushLastSocketGoneEvent();
} }
} /* removeSocket */ } /* removeSocket */
@ -457,6 +461,26 @@ CookieRef::HaveRoom( int nPlayers )
return haveRoom; return haveRoom;
} }
int
CookieRef::CountSockets()
{
RWReadLock rrl( &m_socketsRWLock );
return CountSockets_locked();
}
int
CookieRef::CountSockets_locked()
{
int count = 0;
for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
HostRec* hr = m_sockets[ii];
if ( !!hr ) {
++count;
}
}
return count;
}
bool bool
CookieRef::HasSocket( const AddrInfo* addr ) CookieRef::HasSocket( const AddrInfo* addr )
{ {
@ -473,9 +497,11 @@ CookieRef::GetAddrs()
{ {
vector<AddrInfo> result; vector<AddrInfo> result;
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
result.push_back( iter->m_addr ); if ( !!hr ) {
result.push_back( hr->m_addr );
}
} }
return result; return result;
} }
@ -485,10 +511,10 @@ CookieRef::HasSocket_locked( const AddrInfo* addr )
{ {
ASSERT_LOCKED(); ASSERT_LOCKED();
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter;
bool found = false; bool found = false;
for ( iter = m_sockets.begin(); !found && iter != m_sockets.end(); ++iter ) { for ( unsigned int ii = 0; !found && ii < VSIZE(m_sockets); ++ii ) {
found = iter->m_addr.equals( *addr ); HostRec* hr = m_sockets[ii];
found = !!hr && hr->m_addr.equals( *addr );
} }
logf( XW_LOGINFO, "%s=>%d", __func__, found ); logf( XW_LOGINFO, "%s=>%d", __func__, found );
@ -929,28 +955,29 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
*devIDp = devID; *devIDp = devID;
} }
evt->u.con.srcID = HostID hostid =
DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID, DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
evt->u.con.clientVersion, nPlayersH, seed, evt->u.con.clientVersion, nPlayersH, seed,
addr, devID, reconn ); addr, devID, reconn );
evt->u.con.srcID = hostid;
HostID hostid = evt->u.con.srcID;
if ( NULL != hidp ) { if ( NULL != hidp ) {
*hidp = hostid; *hidp = hostid;
} }
/* first add the rec here, whether it'll get ack'd or not */ /* first add the rec here, whether it'll get ack'd or not */
int count = CountSockets();
logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, " logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, "
"(size=%d)", __func__, hostid, m_sockets.size()); "(size=%d)", __func__, hostid, count );
assert( m_sockets.size() < 4 ); assert( count < 4 );
{ {
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
HostRec hr( hostid, addr, nPlayersH, seed, !reconn ); HostRec* hr = new HostRec( hostid, addr, nPlayersH, seed, !reconn );
logf( XW_LOGINFO, "%s: adding socket rec with ts %lx", __func__, logf( XW_LOGINFO, "%s: adding socket rec with ts %lx", __func__,
addr->created() ); addr->created() );
m_sockets.push_back( hr ); assert( NULL == m_sockets[hostid-1] );
m_sockets[hostid-1] = hr;
} }
printSeeds(__func__); printSeeds(__func__);
@ -972,16 +999,14 @@ CookieRef::updateAck( HostID hostID, bool keep )
{ {
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
vector<HostRec>::iterator iter; HostRec* hr = m_sockets[hostID-1];
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( !!hr && hr->m_ackPending ) {
if ( iter->m_ackPending && iter->m_hostID == hostID ) { assert( hr->m_hostID == hostID );
if ( keep ) { if ( keep ) {
iter->m_ackPending = false; hr->m_ackPending = false;
DBMgr::Get()->NoteAckd( ConnName(), hostID ); DBMgr::Get()->NoteAckd( ConnName(), hostID );
} else { } else {
nonKeeper = &iter->m_addr; nonKeeper = &hr->m_addr;
}
break;
} }
} }
} }
@ -1244,11 +1269,13 @@ CookieRef::notifyOthers( const AddrInfo* addr, XWRelayMsg msg, XWREASON why )
ASSERT_LOCKED(); ASSERT_LOCKED();
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
const AddrInfo* other = &iter->m_addr; if ( !!hr ) {
const AddrInfo* other = &hr->m_addr;
if ( !other->equals( *addr ) ) { if ( !other->equals( *addr ) ) {
send_msg( other, iter->m_hostID, msg, why, false ); send_msg( other, hr->m_hostID, msg, why, false );
}
} }
} }
} /* notifyOthers */ } /* notifyOthers */
@ -1311,12 +1338,11 @@ CookieRef::sendAllHere( bool initial )
{ {
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; HostRec* hr = m_sockets[dest-1];
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( !!hr ) {
if ( iter->m_hostID == dest ) { assert( hr->m_hostID == dest );
sent = send_with_length( &iter->m_addr, dest, buf, bufp-buf, true ); sent = send_with_length( &hr->m_addr, dest, buf,
break; bufp-buf, true );
}
} }
} }
if ( !sent ) { if ( !sent ) {
@ -1344,9 +1370,10 @@ CookieRef::disconnectSockets( XWREASON why )
{ {
ASSERT_LOCKED(); ASSERT_LOCKED();
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
const AddrInfo* addr = &iter->m_addr; if ( !!hr ) {
const AddrInfo* addr = &hr->m_addr;
if ( addr->socket() != 0 ) { if ( addr->socket() != 0 ) {
disconnectSocket( addr, why ); disconnectSocket( addr, why );
} else { } else {
@ -1354,6 +1381,7 @@ CookieRef::disconnectSockets( XWREASON why )
} }
} }
} }
}
void void
CookieRef::disconnectSocket( const AddrInfo* addr, XWREASON why ) CookieRef::disconnectSocket( const AddrInfo* addr, XWREASON why )
@ -1372,9 +1400,11 @@ CookieRef::removeDevice( const CRefEvent* const evt )
dbmgr->KillGame( ConnName(), evt->u.devgone.hid ); dbmgr->KillGame( ConnName(), evt->u.devgone.hid );
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
notifyGameDead( &iter->m_addr ); if ( !!hr ) {
notifyGameDead( &hr->m_addr );
}
} }
} }
} }
@ -1387,25 +1417,22 @@ CookieRef::noteHeartbeat( const CRefEvent* evt )
ASSERT_LOCKED(); ASSERT_LOCKED();
RWWriteLock rwl( &m_socketsRWLock ); RWWriteLock rwl( &m_socketsRWLock );
vector<HostRec>::iterator iter; HostRec* hr = m_sockets[id-1];
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { if ( !!hr ) {
if ( iter->m_hostID == id ) { assert( hr->m_hostID == id );
if ( iter->m_addr.equals(addr) ) { if ( hr->m_addr.equals(addr) ) {
logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d", logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d",
iter->m_lastHeartbeat, uptime() ); hr->m_lastHeartbeat, uptime() );
iter->m_lastHeartbeat = uptime(); hr->m_lastHeartbeat = uptime();
} else { } else {
/* 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. But: connection. An attack is the most likely explanation. But:
now it's happening after a crash and clients reconnect. */ now it's happening after a crash and clients reconnect. */
logf( XW_LOGERROR, "wrong socket record for HostID %x; " logf( XW_LOGERROR, "wrong socket record for HostID %x; "
"wanted %d, found %d", id, addr.socket(), iter->m_addr.socket() ); "wanted %d, found %d", id, addr.socket(),
hr->m_addr.socket() );
} }
break; } else {
}
}
if ( iter == m_sockets.end() ) {
logf( XW_LOGERROR, "no socket for HostID %x", id ); logf( XW_LOGERROR, "no socket for HostID %x", id );
} }
} /* noteHeartbeat */ } /* noteHeartbeat */
@ -1460,15 +1487,17 @@ CookieRef::printSeeds( const char* caller )
uint16_t bits = 0; uint16_t bits = 0;
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
HostID hostID = iter->m_hostID; if ( !!hr ) {
HostID hostID = hr->m_hostID;
assert( 0 == (bits & (1 << hostID)) ); assert( 0 == (bits & (1 << hostID)) );
bits |= 1 << hostID; bits |= 1 << hostID;
len += snprintf( &buf[len], sizeof(buf)-len, "[%d]%.4x(%d)/%d/%c ", len += snprintf( &buf[len], sizeof(buf)-len, "[%d]%.4x(%d)/%d/%c ",
iter->m_hostID, iter->m_seed, hr->m_hostID, hr->m_seed,
iter->m_seed, iter->m_addr.socket(), hr->m_seed, hr->m_addr.socket(),
iter->m_ackPending?'a':'A' ); hr->m_ackPending?'a':'A' );
}
} }
logf( XW_LOGINFO, "%s: seeds/sockets/ack'd after %s(): %s", __func__, caller, buf ); logf( XW_LOGINFO, "%s: seeds/sockets/ack'd after %s(): %s", __func__, caller, buf );
} }
@ -1520,17 +1549,19 @@ CookieRef::_PrintCookieInfo( string& out )
ASSERT_LOCKED(); 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() ); CountSockets(), uptime() );
out += buf; out += buf;
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
if ( !!hr ) {
snprintf( buf, sizeof(buf), " HostID=%d; socket=%d;last hbeat=%ld\n", snprintf( buf, sizeof(buf), " HostID=%d; socket=%d;last hbeat=%ld\n",
iter->m_hostID, iter->m_addr.socket(), hr->m_hostID, hr->m_addr.socket(),
iter->m_lastHeartbeat ); hr->m_lastHeartbeat );
out += buf; out += buf;
} }
}
} /* PrintCookieInfo */ } /* PrintCookieInfo */
@ -1539,23 +1570,23 @@ CookieRef::_FormatHostInfo( string* hostIds, string* seeds, string* addrs )
{ {
ASSERT_LOCKED(); ASSERT_LOCKED();
RWReadLock rrl( &m_socketsRWLock ); RWReadLock rrl( &m_socketsRWLock );
vector<HostRec>::const_iterator iter; for ( unsigned int ii = 0; ii < VSIZE(m_sockets); ++ii ) {
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { HostRec* hr = m_sockets[ii];
if ( !!hr ) {
if ( !!hostIds ) { if ( !!hostIds ) {
char buf[8]; char buf[8];
snprintf( buf, sizeof(buf), "%d ", iter->m_hostID ); snprintf( buf, sizeof(buf), "%d ", hr->m_hostID );
*hostIds += buf; *hostIds += buf;
} }
if ( !!seeds ) { if ( !!seeds ) {
char buf[6]; char buf[6];
snprintf( buf, sizeof(buf), "%.4X ", iter->m_seed ); snprintf( buf, sizeof(buf), "%.4X ", hr->m_seed );
*seeds += buf; *seeds += buf;
} }
if ( !!addrs ) { if ( !!addrs ) {
int socket = iter->m_addr.socket(); int socket = hr->m_addr.socket();
sockaddr_in name; sockaddr_in name;
socklen_t siz = sizeof(name); socklen_t siz = sizeof(name);
if ( 0 == getpeername( socket, (struct sockaddr*)&name, &siz) ) { if ( 0 == getpeername( socket, (struct sockaddr*)&name, &siz) ) {
@ -1566,3 +1597,4 @@ CookieRef::_FormatHostInfo( string* hostIds, string* seeds, string* addrs )
} }
} }
} }
}

View file

@ -71,6 +71,7 @@ public:
class CookieRef { class CookieRef {
public: public:
vector<AddrInfo> GetAddrs( void ); vector<AddrInfo> GetAddrs( void );
static const int MAX_DEVICES = 4;
private: private:
/* These classes have access to CookieRef. All others should go through /* These classes have access to CookieRef. All others should go through
@ -98,7 +99,8 @@ class CookieRef {
bool HaveRoom( int nPlayers ); bool HaveRoom( int nPlayers );
int CountSockets() { return m_sockets.size(); } int CountSockets();
int CountSockets_locked(); /* needed? */
bool HasSocket( const AddrInfo* addr ); bool HasSocket( const AddrInfo* addr );
bool HasSocket_locked( const AddrInfo* addr ); bool HasSocket_locked( const AddrInfo* addr );
const char* Cookie() const { return m_cookie.c_str(); } const char* Cookie() const { return m_cookie.c_str(); }
@ -278,7 +280,7 @@ class CookieRef {
/* Track sockets (= current connections with games on devices) in this /* Track sockets (= current connections with games on devices) in this
game. There will never be more than four of these */ game. There will never be more than four of these */
pthread_rwlock_t m_socketsRWLock; pthread_rwlock_t m_socketsRWLock;
vector<HostRec> m_sockets; HostRec* m_sockets[MAX_DEVICES];
int m_heartbeat; /* might change per carrier or something. */ int m_heartbeat; /* might change per carrier or something. */
string m_cookie; /* cookie used for initial connections */ string m_cookie; /* cookie used for initial connections */