mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-02 06:20:14 +01:00
85d484a881
reconnect. I was putting both (i.e. the same device twice) in the same game. Now I detect this based on the seed being duplicated and treat the device as having failed to ACK then proceed with the CONNECT as if it were new. Tested pretty heavily but only with two-device games.
767 lines
21 KiB
C++
767 lines
21 KiB
C++
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
|
|
|
/*
|
|
* Copyright 2005-2009 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
|
|
#include "crefmgr.h"
|
|
#include "cref.h"
|
|
#include "mlock.h"
|
|
#include "configs.h"
|
|
#include "timermgr.h"
|
|
|
|
class SocketStuff {
|
|
public:
|
|
SocketStuff( CookieRef* cref )
|
|
: m_cref(cref)
|
|
{
|
|
pthread_mutex_init( &m_writeMutex, NULL );
|
|
}
|
|
~SocketStuff() { pthread_mutex_destroy( &m_writeMutex ); }
|
|
CookieRef* m_cref;
|
|
pthread_mutex_t m_writeMutex; /* so only one thread writes at a time */
|
|
};
|
|
|
|
static CRefMgr* s_instance = NULL;
|
|
|
|
/* static */ CRefMgr*
|
|
CRefMgr::Get()
|
|
{
|
|
if ( s_instance == NULL ) {
|
|
s_instance = new CRefMgr();
|
|
}
|
|
return s_instance;
|
|
} /* Get */
|
|
|
|
CRefMgr::CRefMgr()
|
|
: m_nextCID(0)
|
|
, m_nRoomsFilled(0)
|
|
, m_startTime(time(NULL))
|
|
{
|
|
/* should be using pthread_once() here */
|
|
pthread_mutex_init( &m_SocketStuffMutex, NULL );
|
|
pthread_mutex_init( &m_nextCIDMutex, NULL );
|
|
pthread_mutex_init( &m_roomsFilledMutex, NULL );
|
|
pthread_mutex_init( &m_freeList_mutex, NULL );
|
|
pthread_rwlock_init( &m_cookieMapRWLock, NULL );
|
|
m_db = DBMgr::Get();
|
|
}
|
|
|
|
CRefMgr::~CRefMgr()
|
|
{
|
|
assert( this == s_instance );
|
|
|
|
delete m_db;
|
|
|
|
pthread_mutex_destroy( &m_freeList_mutex );
|
|
pthread_rwlock_destroy( &m_cookieMapRWLock );
|
|
|
|
SocketMap::iterator iter;
|
|
for ( iter = m_SocketStuff.begin(); iter != m_SocketStuff.end(); ++iter ) {
|
|
SocketStuff* stuff = iter->second;
|
|
delete stuff;
|
|
}
|
|
|
|
s_instance = NULL;
|
|
}
|
|
|
|
void
|
|
CRefMgr::CloseAll()
|
|
{
|
|
/* Get every cref instance, shut it down */
|
|
|
|
for ( ; ; ) {
|
|
CookieRef* cref = NULL;
|
|
{
|
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
|
CookieMap::iterator iter = m_cookieMap.begin();
|
|
if ( iter == m_cookieMap.end() ) {
|
|
break;
|
|
}
|
|
cref = iter->second;
|
|
{
|
|
SafeCref scr( cref ); /* cref */
|
|
scr.Shutdown();
|
|
}
|
|
}
|
|
}
|
|
} /* CloseAll */
|
|
|
|
CookieID
|
|
CRefMgr::nextCID( const char* connName )
|
|
{
|
|
/* Later may want to guarantee that wrap-around doesn't cause an overlap.
|
|
But that's really only a theoretical possibility. */
|
|
MutexLock ml(&m_nextCIDMutex);
|
|
return ++m_nextCID;
|
|
} /* nextCID */
|
|
|
|
void
|
|
CRefMgr::IncrementFullCount( void )
|
|
{
|
|
MutexLock ml( &m_roomsFilledMutex );
|
|
++m_nRoomsFilled;
|
|
}
|
|
|
|
int
|
|
CRefMgr::GetNumRoomsFilled( void )
|
|
{
|
|
MutexLock ml(&m_roomsFilledMutex);
|
|
return m_nRoomsFilled;
|
|
}
|
|
|
|
int
|
|
CRefMgr::GetSize( void )
|
|
{
|
|
return m_cookieMap.size();
|
|
}
|
|
|
|
void
|
|
CRefMgr::GetStats( CrefMgrInfo& mgrInfo )
|
|
{
|
|
mgrInfo.m_nRoomsFilled = GetNumRoomsFilled();
|
|
mgrInfo.m_startTimeSpawn = m_startTime;
|
|
|
|
if ( 0 == m_ports.length() ) {
|
|
RelayConfigs* cfg = RelayConfigs::GetConfigs();
|
|
vector<int> ints;
|
|
if ( cfg->GetValueFor( "GAME_PORTS", ints ) ) {
|
|
vector<int>::const_iterator iter;
|
|
for ( iter = ints.begin(); ; ) {
|
|
char buf[8];
|
|
snprintf( buf, sizeof(buf), "%d", *iter );
|
|
m_ports += buf;
|
|
++iter;
|
|
if ( iter == ints.end() ) {
|
|
break;
|
|
}
|
|
m_ports += ",";
|
|
}
|
|
}
|
|
}
|
|
mgrInfo.m_ports = m_ports.c_str();
|
|
|
|
RWReadLock rwl( &m_cookieMapRWLock );
|
|
mgrInfo.m_nCrefsCurrent = m_cookieMap.size();
|
|
|
|
CookieMap::iterator iter;
|
|
for ( iter = m_cookieMap.begin(); iter != m_cookieMap.end(); ++iter ) {
|
|
CookieRef* cref = iter->second;
|
|
|
|
CrefInfo info;
|
|
info.m_cookie = cref->Cookie();
|
|
info.m_connName = cref->ConnName();
|
|
info.m_cookieID = cref->GetCookieID();
|
|
info.m_curState = cref->CurState();
|
|
info.m_nPlayersSought = cref->GetPlayersSought();
|
|
info.m_nPlayersHere = cref->GetPlayersHere();
|
|
info.m_startTime = cref->GetStarttime();
|
|
info.m_langCode = cref->GetLangCode();
|
|
|
|
SafeCref sc(cref);
|
|
sc.GetHostsConnected( &info.m_hostsIds, &info.m_hostSeeds,
|
|
&info.m_hostIps );
|
|
|
|
mgrInfo.m_crefInfo.push_back( info );
|
|
}
|
|
}
|
|
|
|
CookieID
|
|
CRefMgr::cookieIDForConnName( const char* connName )
|
|
{
|
|
CookieID cid = 0;
|
|
/* for now, just walk the existing data structure and see if the thing's
|
|
in use. If it isn't, return a new id. */
|
|
|
|
RWReadLock rwl( &m_cookieMapRWLock );
|
|
|
|
CookieMap::iterator iter = m_cookieMap.begin();
|
|
while ( iter != m_cookieMap.end() ) {
|
|
CookieRef* cref = iter->second;
|
|
if ( 0 == strcmp( cref->ConnName(), connName ) ) {
|
|
cid = iter->first;
|
|
break;
|
|
}
|
|
++iter;
|
|
}
|
|
|
|
return cid;
|
|
} /* 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;
|
|
}
|
|
|
|
/* connect case */
|
|
CookieRef*
|
|
CRefMgr::getMakeCookieRef( const char* cookie, HostID hid, int socket,
|
|
int nPlayersH, int nPlayersT, int langCode,
|
|
int seed, bool wantsPublic,
|
|
bool makePublic, bool* seenSeed )
|
|
{
|
|
CookieRef* cref;
|
|
CookieID cid;
|
|
char connNameBuf[MAX_CONNNAME_LEN+1] = {0};
|
|
int alreadyHere = 0;
|
|
|
|
/* We have a cookie from a new connection or from a reconnect. This may
|
|
be the first time 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 on
|
|
the same or a different socket. If there's a game, cool. Otherwise add
|
|
a new one. Pass the connName which will be used if set, but if not set
|
|
we'll be generating another later when the game is complete.
|
|
*/
|
|
|
|
*seenSeed = m_db->SeenSeed( cookie, seed, langCode, nPlayersT,
|
|
wantsPublic, connNameBuf,
|
|
sizeof(connNameBuf), &alreadyHere, &cid );
|
|
if ( !*seenSeed ) {
|
|
cid = m_db->FindOpen( cookie, langCode, nPlayersT, nPlayersH,
|
|
wantsPublic, connNameBuf, sizeof(connNameBuf),
|
|
&alreadyHere );
|
|
}
|
|
|
|
if ( cid > 0 ) {
|
|
cref = getCookieRef_impl( cid );
|
|
} else {
|
|
cid = nextCID( NULL );
|
|
cref = AddNew( cookie, connNameBuf, cid, langCode,
|
|
nPlayersT, alreadyHere );
|
|
if ( !connNameBuf[0] ) { /* didn't exist in DB */
|
|
m_db->AddNew( cookie, cref->ConnName(), cid, langCode, nPlayersT,
|
|
wantsPublic || makePublic );
|
|
} else {
|
|
m_db->AddCID( connNameBuf, cid );
|
|
}
|
|
}
|
|
|
|
return cref;
|
|
} /* getMakeCookieRef */
|
|
|
|
/* reconnect case */
|
|
CookieRef*
|
|
CRefMgr::getMakeCookieRef( const char* connName, const char* cookie,
|
|
HostID hid, int socket, int nPlayersH,
|
|
int nPlayersS, int seed, int langCode,
|
|
bool isPublic, bool* isDead )
|
|
{
|
|
CookieRef* cref = NULL;
|
|
|
|
/* fetch these from DB */
|
|
char curCookie[MAX_INVITE_LEN+1];
|
|
int curLangCode;
|
|
int nPlayersT = 0;
|
|
int nAlreadyHere = 0;
|
|
|
|
CookieID cid = m_db->FindGame( connName, curCookie, sizeof(curCookie),
|
|
&curLangCode, &nPlayersT, &nAlreadyHere,
|
|
isDead );
|
|
if ( 0 != cid ) { /* already open */
|
|
cref = getCookieRef_impl( cid );
|
|
} else {
|
|
/* The entry may not even be in the DB, e.g. if it got deleted.
|
|
Deal with that possibility by taking the caller's word for it. */
|
|
CookieID cid = nextCID( NULL );
|
|
|
|
if ( nPlayersT == 0 ) { /* wasn't in the DB */
|
|
m_db->AddNew( cookie, connName, cid, langCode, nPlayersS, isPublic );
|
|
curLangCode = langCode;
|
|
nPlayersT = nPlayersS;
|
|
} else {
|
|
cookie = curCookie;
|
|
}
|
|
|
|
cref = AddNew( cookie, connName, cid, curLangCode, nPlayersT,
|
|
nAlreadyHere );
|
|
m_db->AddCID( connName, cid );
|
|
}
|
|
return cref;
|
|
} /* getMakeCookieRef */
|
|
|
|
CookieRef*
|
|
CRefMgr::getMakeCookieRef( const char* const connName, bool* isDead )
|
|
{
|
|
CookieRef* cref = NULL;
|
|
char curCookie[MAX_INVITE_LEN+1];
|
|
int curLangCode;
|
|
int nPlayersT = 0;
|
|
int nAlreadyHere = 0;
|
|
|
|
CookieID cid = m_db->FindGame( connName, curCookie, sizeof(curCookie),
|
|
&curLangCode, &nPlayersT, &nAlreadyHere,
|
|
isDead );
|
|
if ( 0 != cid ) { /* already open */
|
|
cref = getCookieRef_impl( cid );
|
|
} else {
|
|
if ( nPlayersT == 0 ) { /* wasn't in the DB */
|
|
/* do nothing; insufficient info to fake it */
|
|
} else {
|
|
CookieID cid = nextCID( NULL );
|
|
cref = AddNew( curCookie, connName, cid, curLangCode, nPlayersT,
|
|
nAlreadyHere );
|
|
m_db->AddCID( connName, cid );
|
|
}
|
|
}
|
|
return cref;
|
|
}
|
|
|
|
bool
|
|
CRefMgr::Associate( int socket, CookieRef* cref )
|
|
{
|
|
MutexLock ml( &m_SocketStuffMutex );
|
|
return Associate_locked( socket, cref );
|
|
}
|
|
|
|
bool
|
|
CRefMgr::Associate_locked( int socket, CookieRef* cref )
|
|
{
|
|
bool isNew = false;
|
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
|
/* This isn't enough. Must provide a way to reuse sockets should a
|
|
genuinely different connection appear. Now maybe we already remove
|
|
this reference when a socket is closed. Test this! Or assert
|
|
something here. Bottom line: need to swallow repeated/duplicate
|
|
connect messages from same host. */
|
|
if ( iter == m_SocketStuff.end() ) {
|
|
SocketStuff* stuff = new SocketStuff( cref );
|
|
m_SocketStuff.insert( pair< int, SocketStuff* >( socket, stuff ) );
|
|
isNew = true;
|
|
} else {
|
|
logf( XW_LOGERROR, "Already have cref/threadID pair for socket %d; "
|
|
"error???", socket );
|
|
}
|
|
return isNew;
|
|
}
|
|
|
|
void
|
|
CRefMgr::Disassociate_locked( int socket, CookieRef* cref )
|
|
{
|
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
|
if ( iter == m_SocketStuff.end() ) {
|
|
logf( XW_LOGERROR, "can't find SocketStuff for socket %d", socket );
|
|
} else {
|
|
SocketStuff* stuff = iter->second;
|
|
assert( cref == NULL || stuff->m_cref == cref );
|
|
delete stuff;
|
|
m_SocketStuff.erase( iter );
|
|
}
|
|
}
|
|
|
|
void
|
|
CRefMgr::Disassociate( int socket, CookieRef* cref )
|
|
{
|
|
MutexLock ml( &m_SocketStuffMutex );
|
|
Disassociate_locked( socket, cref );
|
|
}
|
|
|
|
void
|
|
CRefMgr::MoveSockets( vector<int> sockets, CookieRef* cref )
|
|
{
|
|
MutexLock ml( &m_SocketStuffMutex );
|
|
vector<int>::iterator iter;
|
|
for ( iter = sockets.begin(); iter != sockets.end(); ++iter ) {
|
|
Disassociate_locked( *iter, NULL );
|
|
Associate_locked( *iter, cref );
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
pthread_mutex_t*
|
|
CRefMgr::GetWriteMutexForSocket( int socket )
|
|
{
|
|
pthread_mutex_t* mutex = NULL;
|
|
MutexLock ml( &m_SocketStuffMutex );
|
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
|
if ( iter != m_SocketStuff.end() ) {
|
|
SocketStuff* stuff = iter->second;
|
|
/* 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" );
|
|
return mutex;
|
|
} /* GetWriteMutexForSocket */
|
|
#endif
|
|
|
|
void
|
|
CRefMgr::RemoveSocketRefs( int socket )
|
|
{
|
|
{
|
|
SafeCref scr( socket );
|
|
scr.Remove( socket );
|
|
}
|
|
|
|
Disassociate( socket, NULL );
|
|
}
|
|
|
|
void
|
|
CRefMgr::PrintSocketInfo( int socket, string& out )
|
|
{
|
|
SafeCref scr( socket );
|
|
const char* name = scr.Cookie();
|
|
if ( name != NULL && name[0] != '\0' ) {
|
|
char buf[64];
|
|
|
|
snprintf( buf, sizeof(buf), "* socket: %d\n", socket );
|
|
out += buf;
|
|
|
|
snprintf( buf, sizeof(buf), " in cookie: %s\n", name );
|
|
out += buf;
|
|
}
|
|
}
|
|
|
|
/* static */ SocketsIterator
|
|
CRefMgr::MakeSocketsIterator()
|
|
{
|
|
pthread_mutex_lock( &m_SocketStuffMutex );
|
|
SocketsIterator iter( m_SocketStuff.begin(), m_SocketStuff.end(),
|
|
&m_SocketStuffMutex );
|
|
return iter;
|
|
}
|
|
|
|
CookieRef*
|
|
CRefMgr::getCookieRef( CookieID cookieID )
|
|
{
|
|
return getCookieRef_impl( cookieID );
|
|
} /* getCookieRef */
|
|
|
|
CookieRef*
|
|
CRefMgr::getCookieRef( int socket )
|
|
{
|
|
CookieRef* cref = NULL;
|
|
MutexLock ml( &m_SocketStuffMutex );
|
|
SocketMap::iterator iter = m_SocketStuff.find( socket );
|
|
if ( iter != m_SocketStuff.end() ) {
|
|
SocketStuff* stuff = iter->second;
|
|
cref = stuff->m_cref;
|
|
}
|
|
|
|
return cref;
|
|
} /* getCookieRef */
|
|
|
|
#ifdef RELAY_HEARTBEAT
|
|
/* static */ void
|
|
CRefMgr::heartbeatProc( void* closure )
|
|
{
|
|
CRefMgr* self = (CRefMgr*)closure;
|
|
self->checkHeartbeats( ::uptime() );
|
|
} /* heartbeatProc */
|
|
#endif
|
|
|
|
CookieRef*
|
|
CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id,
|
|
int langCode, int nPlayers, int nAlreadyHere )
|
|
{
|
|
if ( 0 == connName[0] ) {
|
|
connName = NULL;
|
|
}
|
|
/* PENDING: should this return a locked cref? */
|
|
logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, cid=%d)", __func__,
|
|
cookie, connName, id );
|
|
|
|
CookieRef* ref = getFromFreeList();
|
|
|
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
|
logf( XW_LOGINFO, "making new cref: %d", id );
|
|
|
|
if ( !!ref ) {
|
|
ref->ReInit( cookie, connName, id, langCode, nPlayers, nAlreadyHere );
|
|
} else {
|
|
ref = new CookieRef( cookie, connName, id, langCode, nPlayers,
|
|
nAlreadyHere );
|
|
}
|
|
|
|
ref->assignConnName();
|
|
|
|
m_cookieMap.insert( pair<CookieID, CookieRef*>(ref->GetCookieID(), ref ) );
|
|
logf( XW_LOGINFO, "%s: paired cookie %s/connName %s with cid %d", __func__,
|
|
(cookie?cookie:"NULL"), connName, ref->GetCookieID() );
|
|
|
|
#ifdef RELAY_HEARTBEAT
|
|
if ( m_cookieMap.size() == 1 ) {
|
|
RelayConfigs* cfg = RelayConfigs::GetConfigs();
|
|
int heartbeat;
|
|
cfg->GetValueFor( "HEARTBEAT", &heartbeat );
|
|
TimerMgr::GetTimerMgr()->SetTimer( heartbeat, heartbeatProc, this,
|
|
heartbeat );
|
|
}
|
|
#endif
|
|
|
|
logf( XW_LOGINFO, "%s=>%p", __func__, ref );
|
|
return ref;
|
|
} /* AddNew */
|
|
|
|
void
|
|
CRefMgr::Recycle_locked( CookieRef* cref )
|
|
{
|
|
logf( XW_LOGINFO, "%s(cref=%p,cookie=%s)", __func__, cref, cref->Cookie() );
|
|
CookieID id = cref->GetCookieID();
|
|
DBMgr::Get()->ClearCID( cref->ConnName() );
|
|
cref->Clear();
|
|
addToFreeList( cref );
|
|
|
|
cref->Unlock();
|
|
|
|
/* don't grab this lock until after releasing cref's lock; otherwise
|
|
deadlock happens. */
|
|
RWWriteLock rwl( &m_cookieMapRWLock );
|
|
|
|
CookieMap::iterator iter = m_cookieMap.begin();
|
|
while ( iter != m_cookieMap.end() ) {
|
|
CookieRef* ref = iter->second;
|
|
if ( ref == cref ) {
|
|
logf( XW_LOGINFO, "%s: erasing cref cid %d", __func__, id );
|
|
m_cookieMap.erase( iter );
|
|
break;
|
|
}
|
|
++iter;
|
|
}
|
|
assert( iter != m_cookieMap.end() ); /* we found something */
|
|
|
|
#ifdef RELAY_HEARTBEAT
|
|
if ( m_cookieMap.size() == 0 ) {
|
|
TimerMgr::GetTimerMgr()->ClearTimer( heartbeatProc, this );
|
|
}
|
|
#endif
|
|
} /* CRefMgr::Recycle */
|
|
|
|
void
|
|
CRefMgr::Recycle( CookieID id )
|
|
{
|
|
CookieRef* cref = getCookieRef( id );
|
|
if ( cref != NULL ) {
|
|
cref->Lock();
|
|
Recycle_locked( cref );
|
|
}
|
|
} /* Delete */
|
|
|
|
void
|
|
CRefMgr::Recycle( const char* connName )
|
|
{
|
|
CookieID id = cookieIDForConnName( connName );
|
|
Recycle( id );
|
|
} /* Delete */
|
|
|
|
CookieRef*
|
|
CRefMgr::getCookieRef_impl( CookieID cookieID )
|
|
{
|
|
CookieRef* ref = NULL;
|
|
RWReadLock rwl( &m_cookieMapRWLock );
|
|
|
|
CookieMap::iterator iter = m_cookieMap.find( cookieID );
|
|
while ( iter != m_cookieMap.end() ) {
|
|
CookieRef* second = iter->second;
|
|
if ( second->GetCookieID() == cookieID ) {
|
|
ref = second;
|
|
break;
|
|
}
|
|
++iter;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
#ifdef RELAY_HEARTBEAT
|
|
void
|
|
CRefMgr::checkHeartbeats( time_t now )
|
|
{
|
|
vector<CookieRef*> crefs;
|
|
|
|
{
|
|
RWReadLock rwl( &m_cookieMapRWLock );
|
|
CookieMap::iterator iter = m_cookieMap.begin();
|
|
while ( iter != m_cookieMap.end() ) {
|
|
crefs.push_back(iter->second);
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
unsigned int ii;
|
|
for ( ii = 0; ii < crefs.size(); ++ii ) {
|
|
SafeCref scr( crefs[ii] );
|
|
scr.CheckHeartbeats( now );
|
|
}
|
|
} /* checkHeartbeats */
|
|
#endif
|
|
|
|
time_t
|
|
CRefMgr::uptime( void )
|
|
{
|
|
return time(NULL) - m_startTime;
|
|
}
|
|
|
|
/* static */ CookieMapIterator
|
|
CRefMgr::GetCookieIterator()
|
|
{
|
|
CookieMapIterator iter(&m_cookieMapRWLock);
|
|
return iter;
|
|
}
|
|
|
|
|
|
CookieMapIterator::CookieMapIterator(pthread_rwlock_t* rwlock)
|
|
: m_rwl( rwlock )
|
|
,_iter( CRefMgr::Get()->m_cookieMap.begin() )
|
|
{
|
|
}
|
|
|
|
CookieID
|
|
CookieMapIterator::Next()
|
|
{
|
|
CookieID id = 0;
|
|
if ( _iter != CRefMgr::Get()->m_cookieMap.end() ) {
|
|
CookieRef* cref = _iter->second;
|
|
id = cref->GetCookieID();
|
|
++_iter;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// SafeCref
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
/* connect case */
|
|
SafeCref::SafeCref( const char* cookie, int socket, int nPlayersH, int nPlayersS,
|
|
unsigned short gameSeed, int langCode, bool wantsPublic,
|
|
bool makePublic )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
, m_seenSeed( false )
|
|
{
|
|
CookieRef* cref;
|
|
|
|
cref = m_mgr->getMakeCookieRef( cookie, 0, socket,
|
|
nPlayersH, nPlayersS, langCode,
|
|
gameSeed, wantsPublic, makePublic,
|
|
&m_seenSeed );
|
|
if ( cref != NULL ) {
|
|
m_locked = cref->Lock();
|
|
m_cref = cref;
|
|
m_isValid = true;
|
|
}
|
|
}
|
|
|
|
/* REconnect case */
|
|
SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid,
|
|
int socket, int nPlayersH, int nPlayersS,
|
|
unsigned short gameSeed, int langCode,
|
|
bool wantsPublic, bool makePublic )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
{
|
|
CookieRef* cref;
|
|
assert( hid <= 4 ); /* no more than 4 hosts */
|
|
|
|
bool isDead = false;
|
|
cref = m_mgr->getMakeCookieRef( connName, cookie, hid, socket, nPlayersH,
|
|
nPlayersS, gameSeed, langCode,
|
|
wantsPublic || makePublic, &isDead );
|
|
if ( cref != NULL ) {
|
|
m_locked = cref->Lock();
|
|
m_cref = cref;
|
|
m_isValid = true;
|
|
m_dead = isDead;
|
|
}
|
|
}
|
|
|
|
/* ConnName case -- must exist */
|
|
SafeCref::SafeCref( const char* const connName )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
{
|
|
bool isDead = false;
|
|
CookieRef* cref = m_mgr->getMakeCookieRef( connName, &isDead );
|
|
if ( cref != NULL ) {
|
|
m_locked = cref->Lock();
|
|
m_cref = cref;
|
|
m_isValid = true;
|
|
m_dead = isDead;
|
|
}
|
|
}
|
|
|
|
SafeCref::SafeCref( CookieID connID, bool failOk )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
{
|
|
CookieRef* cref = m_mgr->getCookieRef( connID );
|
|
if ( cref != NULL ) { /* known cookie? */
|
|
m_locked = cref->Lock();
|
|
m_isValid = m_locked && connID == cref->GetCookieID();
|
|
m_cref = cref;
|
|
}
|
|
}
|
|
|
|
SafeCref::SafeCref( int socket )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
{
|
|
CookieRef* cref = m_mgr->getCookieRef( socket );
|
|
if ( cref != NULL ) { /* known socket? */
|
|
m_locked = cref->Lock();
|
|
m_isValid = m_locked && cref->HasSocket_locked( socket );
|
|
m_cref = cref;
|
|
}
|
|
}
|
|
|
|
SafeCref::SafeCref( CookieRef* cref )
|
|
: m_cref( NULL )
|
|
, m_mgr( CRefMgr::Get() )
|
|
, m_isValid( false )
|
|
{
|
|
m_locked = cref->Lock();
|
|
m_isValid = m_locked;
|
|
m_cref = cref;
|
|
}
|
|
|
|
SafeCref::~SafeCref()
|
|
{
|
|
if ( m_cref != NULL && m_locked ) {
|
|
if ( m_cref->ShouldDie() ) {
|
|
m_mgr->Recycle_locked( m_cref );
|
|
} else {
|
|
m_cref->Unlock();
|
|
}
|
|
}
|
|
}
|