use deviceID to invite via relay; make 3- and 4-device games work

This commit is contained in:
katianderic 2020-02-13 21:43:46 -08:00
parent b1bd506e22
commit c7c1946793
10 changed files with 198 additions and 106 deletions

View file

@ -536,15 +536,23 @@ gi_copy( MPFORMAL CurGameInfo* destGI, const CurGameInfo* srcGI )
void
gi_setNPlayers( CurGameInfo* gi, XP_U16 nTotal, XP_U16 nHere )
{
XP_ASSERT( nTotal < MAX_NUM_PLAYERS );
XP_ASSERT( nTotal <= MAX_NUM_PLAYERS );
XP_ASSERT( nHere < nTotal );
gi->nPlayers = nTotal;
XP_U16 ii;
for ( ii = 0; ii < nTotal; ++ii ) {
gi->players[ii].isLocal = ii < nHere;
XP_ASSERT( !LP_IS_ROBOT(&gi->players[ii]) );
XP_U16 curLocal = 0;
for ( XP_U16 ii = 0; ii < nTotal; ++ii ) {
if ( gi->players[ii].isLocal ) {
++curLocal;
}
}
if ( nHere != curLocal ) {
XP_ASSERT(0);
/* for ( XP_U16 ii = 0; ii < nTotal; ++ii ) { */
/* gi->players[ii].isLocal = ii < nHere; */
/* } */
}
}
@ -750,6 +758,23 @@ player_timePenalty( CurGameInfo* gi, XP_U16 playerNum )
return result;
} /* player_timePenalty */
void
game_logGI( const CurGameInfo* gi, const char* msg )
{
XP_LOGFF( "msg: %s", msg );
XP_LOGF( " nPlayers: %d", gi->nPlayers );
for ( XP_U16 ii = 0; ii < gi->nPlayers; ++ii ) {
const LocalPlayer* lp = &gi->players[ii];
XP_LOGF( " player[%d]: local: %d; robotIQ: %d; name: %s", ii,
lp->isLocal, lp->robotIQ, lp->name );
}
XP_LOGF( " forceChannel: %d", gi->forceChannel );
XP_LOGF( " serverRole: %d", gi->serverRole );
XP_LOGF( " gameID: %d", gi->gameID );
XP_LOGF( " dictName: %s", gi->dictName );
}
#ifdef CPLUS
}
#endif

View file

@ -63,6 +63,12 @@ typedef struct CurGameInfo {
XP_Bool confirmBTConnect; /* only used for BT */
} CurGameInfo;
#ifdef DEBUG
void game_logGI( const CurGameInfo* gi, const char* msg );
#else
# define game_logGI(gi, msg)
#endif
#ifdef CPLUS
}
#endif

View file

@ -32,9 +32,11 @@
/* Figure out how many lines there are and how wide the widest is.
*/
int
cursesask( WINDOW* window, const char* question, short numButtons,
cursesask( WINDOW* parentWin, const char* question, short numButtons,
const char** buttons )
{
XP_LOGFF( "(question=%s, parentWin=%p)", question, parentWin );
XP_ASSERT( !!parentWin );
WINDOW* confWin;
int x, y, rows, row, nLines;
int left, top;
@ -46,8 +48,8 @@ cursesask( WINDOW* window, const char* question, short numButtons,
FormatInfo fi;
int len;
getmaxyx( window, y, x);
getbegyx( window, top, left );
getmaxyx( parentWin, y, x);
getbegyx( parentWin, top, left );
measureAskText( question, x-2, &fi );
len = fi.maxLen;
@ -122,8 +124,8 @@ cursesask( WINDOW* window, const char* question, short numButtons,
delwin( confWin );
/* this leaves a ghost line, but I can't figure out a better way. */
wtouchln( window, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( window );
wtouchln( parentWin, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( parentWin );
return curSelButton;
} /* cursesask */

View file

@ -84,6 +84,54 @@ static void setupBoard( CursesBoardGlobals* bGlobals );
static void initMenus( CursesBoardGlobals* bGlobals );
static void disposeDraw( CursesBoardGlobals* bGlobals );
#ifdef KEYBOARD_NAV
static bool handleLeft( void* closure, int key );
static bool handleRight( void* closure, int key );
static bool handleUp( void* closure, int key );
static bool handleDown( void* closure, int key );
static bool handleClose( void* closure, int key );
#endif
#ifdef ALT_KEYS_WORKING
static bool handleAltLeft( void* closure, int key );
static bool handleAltRight( void* closure, int key );
static bool handleAltUp( void* closure, int key );
static bool handleAltDown( void* closure, int key );
#endif
static bool handleJuggle( void* closure, int key );
static bool handleHide( void* closure, int key );
/* static bool handleResend( void* closure, int key ); */
static bool handleSpace( void* closure, int key );
static bool handleRet( void* closure, int key );
static bool handleShowVals( void* closure, int key );
static bool handleHint( void* closure, int key );
static bool handleCommit( void* closure, int key );
static bool handleFlip( void* closure, int key );
static bool handleToggleValues( void* closure, int key );
static bool handleBackspace( void* closure, int key );
static bool handleUndo( void* closure, int key );
static bool handleReplace( void* closure, int key );
#ifdef CURSES_SMALL_SCREEN
static bool handleRootKeyShow( void* closure, int key );
static bool handleRootKeyHide( void* closure, int key );
#endif
static bool handleInvite( void* closure, int key );
static void relay_connd_curses( void* closure, XP_UCHAR* const room,
XP_Bool reconnect, XP_U16 devOrder,
XP_Bool allHere, XP_U16 nMissing );
static void relay_status_curses( void* closure, CommsRelayState state );
static void relay_error_curses( void* closure, XWREASON relayErr );
static XP_Bool relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len,
const XP_UCHAR* msgNo,
const XP_UCHAR* relayID, void* closure );
static void curses_countChanged( void* closure, XP_U16 newCount );
static XP_U32 curses_getFlags( void* closure );
#ifdef RELAY_VIA_HTTP
static void relay_requestJoin_curses( void* closure, const XP_UCHAR* devID,
const XP_UCHAR* room, XP_U16 nPlayersHere,
XP_U16 nPlayersTotal, XP_U16 seed, XP_U16 lang );
#endif
CursesBoardState*
cb_init( CursesAppGlobals* aGlobals, LaunchParams* params,
CursesMenuState* menuState, OnGameSaved onGameSaved )
@ -106,6 +154,16 @@ cb_resized( CursesBoardState* cbState, const cb_dims* dims )
}
}
static gint
inviteIdle( gpointer data )
{
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
if ( !!bGlobals->cGlobals.params->connInfo.relay.inviteeRelayIDs ) {
handleInvite( bGlobals, 0 );
}
return FALSE;
}
void
cb_open( CursesBoardState* cbState, sqlite3_int64 rowid, const cb_dims* dims )
{
@ -119,6 +177,9 @@ cb_open( CursesBoardState* cbState, sqlite3_int64 rowid, const cb_dims* dims )
if ( !!cGlobals->game.comms ) {
comms_resendAll( cGlobals->game.comms, COMMS_CONN_NONE, XP_FALSE );
}
if ( bGlobals->cGlobals.params->forceInvite ) {
(void)ADD_ONETIME_IDLE( inviteIdle, bGlobals);
}
}
bool
@ -157,54 +218,6 @@ cb_newFor( CursesBoardState* cbState, const NetLaunchInfo* nli,
setupBoard( bGlobals );
}
#ifdef KEYBOARD_NAV
static bool handleLeft( void* closure, int key );
static bool handleRight( void* closure, int key );
static bool handleUp( void* closure, int key );
static bool handleDown( void* closure, int key );
static bool handleClose( void* closure, int key );
#endif
#ifdef ALT_KEYS_WORKING
static bool handleAltLeft( void* closure, int key );
static bool handleAltRight( void* closure, int key );
static bool handleAltUp( void* closure, int key );
static bool handleAltDown( void* closure, int key );
#endif
static bool handleJuggle( void* closure, int key );
static bool handleHide( void* closure, int key );
/* static bool handleResend( void* closure, int key ); */
static bool handleSpace( void* closure, int key );
static bool handleRet( void* closure, int key );
static bool handleShowVals( void* closure, int key );
static bool handleHint( void* closure, int key );
static bool handleCommit( void* closure, int key );
static bool handleFlip( void* closure, int key );
static bool handleToggleValues( void* closure, int key );
static bool handleBackspace( void* closure, int key );
static bool handleUndo( void* closure, int key );
static bool handleReplace( void* closure, int key );
static bool handleInvite( void* closure, int key );
#ifdef CURSES_SMALL_SCREEN
static bool handleRootKeyShow( void* closure, int key );
static bool handleRootKeyHide( void* closure, int key );
#endif
static void relay_connd_curses( void* closure, XP_UCHAR* const room,
XP_Bool reconnect, XP_U16 devOrder,
XP_Bool allHere, XP_U16 nMissing );
static void relay_status_curses( void* closure, CommsRelayState state );
static void relay_error_curses( void* closure, XWREASON relayErr );
static XP_Bool relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len,
const XP_UCHAR* msgNo,
const XP_UCHAR* relayID, void* closure );
static void curses_countChanged( void* closure, XP_U16 newCount );
static XP_U32 curses_getFlags( void* closure );
#ifdef RELAY_VIA_HTTP
static void relay_requestJoin_curses( void* closure, const XP_UCHAR* devID,
const XP_UCHAR* room, XP_U16 nPlayersHere,
XP_U16 nPlayersTotal, XP_U16 seed, XP_U16 lang );
#endif
const MenuList g_allMenuList[] = {
{ handleLeft, "Left", "H", 'H' },
{ handleRight, "Right", "L", 'L' },
@ -594,8 +607,10 @@ enableDraw( CursesBoardGlobals* bGlobals, const cb_dims* dims )
getmaxyx( bGlobals->boardWin, bGlobals->winHeight, bGlobals->winWidth );
CommonGlobals* cGlobals = &bGlobals->cGlobals;
cGlobals->draw = cursesDrawCtxtMake( bGlobals->boardWin );
board_setDraw( cGlobals->game.board, cGlobals->draw );
if( !!bGlobals->boardWin ) {
cGlobals->draw = cursesDrawCtxtMake( bGlobals->boardWin );
board_setDraw( cGlobals->game.board, cGlobals->draw );
}
setupBoard( bGlobals );
}
@ -1188,9 +1203,16 @@ handleInvite( void* closure, int XP_UNUSED(key) )
/* These should both be settable/derivable */
linux_sms_invite( params, &nli, params->connInfo.sms.inviteePhone,
params->connInfo.sms.port );
} else if ( 0 != params->connInfo.relay.inviteeRelayID ) {
relaycon_invite( params, params->connInfo.relay.inviteeRelayID, NULL, &nli );
} else if ( !!params->connInfo.relay.inviteeRelayIDs ) {
GSList* inviteeRelayIDs = params->connInfo.relay.inviteeRelayIDs;
for ( int ii = 0; ii < g_slist_length(inviteeRelayIDs); ++ii ) {
XP_U16 nPlayers = 1;
gint forceChannel = ii + 1;
NetLaunchInfo nli = {0};
nli_init( &nli, cGlobals->gi, &addr, nPlayers, forceChannel );
uint64_t inviteeRelayID = (uint64_t)g_slist_nth_data(inviteeRelayIDs, ii);
relaycon_invite( params, (XP_U32)inviteeRelayID, NULL, &nli );
}
/* Try sending to self, using the phone number or relayID of this device */
} else if ( addr_hasType( &addr, COMMS_CONN_SMS ) ) {
linux_sms_invite( params, &nli, addr.u.sms.phone, addr.u.sms.port );

View file

@ -190,16 +190,18 @@ static void
initCurses( CursesAppGlobals* aGlobals )
{
/* ncurses man page says most apps want this sequence */
aGlobals->mainWin = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE); /* effects wgetch only? */
if ( !aGlobals->cag.params->closeStdin ) {
aGlobals->mainWin = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE); /* effects wgetch only? */
getmaxyx( aGlobals->mainWin, aGlobals->winHeight, aGlobals->winWidth );
XP_LOGF( "%s: getmaxyx()->w:%d; h:%d", __func__, aGlobals->winWidth,
aGlobals->winHeight );
getmaxyx( aGlobals->mainWin, aGlobals->winHeight, aGlobals->winWidth );
XP_LOGF( "%s: getmaxyx()->w:%d; h:%d", __func__, aGlobals->winWidth,
aGlobals->winHeight );
}
/* globals->statusLine = height - MENU_WINDOW_HEIGHT - 1; */
/* globals->menuWin = newwin( MENU_WINDOW_HEIGHT, width, */
@ -258,11 +260,10 @@ handleOpenGame( void* closure, int XP_UNUSED(key) )
LOG_FUNC();
CursesAppGlobals* aGlobals = (CursesAppGlobals*)closure;
const GameInfo* gi = cgl_getSel( aGlobals->gameList );
if ( !!gi ) {
cb_dims dims;
figureDims( aGlobals, &dims );
cb_open( aGlobals->cbState, gi->rowid, &dims );
}
XP_ASSERT( !!gi );
cb_dims dims;
figureDims( aGlobals, &dims );
cb_open( aGlobals->cbState, gi->rowid, &dims );
return XP_TRUE;
}
@ -1160,7 +1161,7 @@ relayInviteReceivedCurses( void* closure, NetLaunchInfo* invite )
int nRowIDs = VSIZE(rowids);
getRowsForGameID( aGlobals->cag.params->pDb, invite->gameID, rowids, &nRowIDs );
bool doIt = 0 == nRowIDs;
if ( ! doIt ) {
if ( ! doIt && !!aGlobals->mainWin ) {
const gchar* question = "Duplicate invitation received. Accept anyway?";
const char* buttons[] = { "Yes", "No" };
doIt = 0 == cursesask( aGlobals->mainWin, question, VSIZE(buttons), buttons );
@ -1286,10 +1287,9 @@ cursesDevIDReceived( void* closure, const XP_UCHAR* devID,
XP_U16 maxInterval )
{
CursesAppGlobals* aGlobals = (CursesAppGlobals*)closure;
// CommonGlobals* cGlobals = &globals->cGlobals;
sqlite3* pDb = aGlobals->cag.params->pDb;
if ( !!devID ) {
XP_LOGF( "%s(devID=%s)", __func__, devID );
XP_LOGF( "%s(devID='%s')", __func__, devID );
/* If we already have one, make sure it's the same! Else store. */
gchar buf[64];
@ -1297,10 +1297,12 @@ cursesDevIDReceived( void* closure, const XP_UCHAR* devID,
&& 0 == strcmp( buf, devID );
if ( !have ) {
db_store( pDb, KEY_RDEVID, devID );
XP_LOGFF( "storing new devid: %s", devID );
cgl_draw( aGlobals->gameList );
}
(void)g_timeout_add_seconds( maxInterval, keepalive_timer, aGlobals );
} else {
XP_LOGF( "%s: bad relayid", __func__ );
XP_LOGFF( "%s", "bad relayid" );
db_remove( pDb, KEY_RDEVID );
DevIDType typ;

View file

@ -151,6 +151,7 @@ ensureLocalPlayerNames( LaunchParams* XP_UNUSED_DBG(params), CurGameInfo* gi )
}
}
#if 0
static bool
canMakeFromGI( const CurGameInfo* gi )
{
@ -171,8 +172,10 @@ canMakeFromGI( const CurGameInfo* gi )
result = result && (haveDict || allHaveDicts);
LOG_RETURNF( "%d", result );
XP_ASSERT( result );
return result;
}
#endif
bool
linuxOpenGame( CommonGlobals* cGlobals, const TransportProcs* procs,
@ -215,7 +218,7 @@ linuxOpenGame( CommonGlobals* cGlobals, const TransportProcs* procs,
stream_destroy( stream );
}
if ( !opened && canMakeFromGI( cGlobals->gi ) ) {
if ( !opened /* && canMakeFromGI( cGlobals->gi )*/ ) {
opened = XP_TRUE;
#ifdef XWFEATURE_RELAY
@ -841,6 +844,7 @@ typedef enum {
,CMD_DROPRCVSMS
,CMD_FORCECHANNEL
,CMD_FORCE_GAME
,CMD_FORCE_INVITE
#ifdef XWFEATURE_CROSSHAIRS
,CMD_NOCROSSHAIRS
@ -970,6 +974,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_DROPRCVSMS, false, "drop-receive-sms", "start new games with sms receive disabled" }
,{ CMD_FORCECHANNEL, true, "force-channel", "force (clients) to use this hostid/channel" }
,{ CMD_FORCE_GAME, false, "force-game", "if there's no game on launch, create one" }
,{ CMD_FORCE_INVITE, false, "force-invite", "if we can, send an invitation by relay or sms" }
#ifdef XWFEATURE_CROSSHAIRS
,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs",
@ -2817,7 +2822,9 @@ main( int argc, char** argv )
mainParams.bonusFile = optarg;
break;
case CMD_INVITEE_RELAYID:
mainParams.connInfo.relay.inviteeRelayID = atoi(optarg);
mainParams.connInfo.relay.inviteeRelayIDs =
g_slist_append(mainParams.connInfo.relay.inviteeRelayIDs,
(void*)(uint64_t)atoi(optarg));
addr_addType( &mainParams.addr, COMMS_CONN_RELAY );
break;
#endif
@ -2897,6 +2904,10 @@ main( int argc, char** argv )
mainParams.forceNewGame = true;
break;
case CMD_FORCE_INVITE:
mainParams.forceInvite = true;
break;
#ifdef XWFEATURE_CROSSHAIRS
case CMD_NOCROSSHAIRS:
mainParams.hideCrosshairs = XP_TRUE;

View file

@ -113,6 +113,7 @@ typedef struct LaunchParams {
XP_Bool runSMSTest;
XP_Bool noHTTPAuto;
bool forceNewGame;
bool forceInvite;
XP_U16 splitPackets;
XP_U16 chatsInterval; /* 0 means disabled */
XP_U16 askTimeout;
@ -141,7 +142,7 @@ typedef struct LaunchParams {
short defaultSendPort;
XP_Bool seeksPublicRoom;
XP_Bool advertiseRoom;
XP_U32 inviteeRelayID;
GSList* inviteeRelayIDs;
} relay;
#endif
#ifdef XWFEATURE_BLUETOOTH

View file

@ -164,13 +164,14 @@ class Device():
sTilesLeftPoolPat = re.compile('.*pool_r.*Tiles: (\d+) tiles left in pool')
sTilesLeftTrayPat = re.compile('.*player \d+ now has (\d+) tiles')
sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*')
sDevIDPat = re.compile('.*linux_getDevID => ([\da-fA-F]+) .*typ=ID_TYPE_RELAY.*')
sDevIDPat = re.compile('.*storing new devid: ([\da-fA-F]+).*')
sConnPat = re.compile('.*linux_util_informMissing\(isServer.*nMissing=0\).*')
sScoresDup = []
sScoresReg = []
def __init__(self, args, game, indx, params, room, peers, db,
log, script, nInGame, inDupMode):
def __init__(self, args, game, indx, params, room, peers, order,
db, log, script, nInGame, inDupMode, usePublic):
self.game = game
self.indx = indx
self.args = args
@ -178,11 +179,13 @@ class Device():
self.gameOver = False
self.params = params
self.room = room
self.order = order
self.db = db
self.logPath = log
self.script = script
self.nInGame = nInGame
self.inDupMode = inDupMode
self.usePublic = usePublic
# runtime stuff; init now
self.app = args.APP_OLD
self.proc = None
@ -193,6 +196,9 @@ class Device():
self.nTilesLeftPool = None
self.nTilesLeftTray = None
self.relayID = None
self.inviteeDevID = None
self.inviteeDevIDs = [] # only servers use this
self.connected = False
self.relaySeed = 0
self.locked = False
@ -247,11 +253,13 @@ class Device():
self.relaySeed = int(match.group(1))
self.relayID = match.group(2)
if not self.devID:
if not self.inviteeDevID:
match = Device.sDevIDPat.match(line)
if match:
self.devID = int(match.group(1), 16)
print( 'read devid:', self.devID )
if match: self.inviteeDevID = int(match.group(1), 16)
if not self.connected:
match = Device.sConnPat.match(line)
if match: self.connected = True
self.locked = False
@ -277,6 +285,24 @@ class Device():
self.checkScript()
self.launchCount += 1
args = [ self.script, '--close-stdin' ]
# If I'm an unconnected server and I know a client's relayid,
# append it so invitation can happen. When more than one
# device will be invited, the invitations must always go in
# the same order so channels will be assigned consistently. So
# keep them in an array as they're encountered, and use in
# that order
if not self.usePublic and self.order == 1 and self.inviteeDevID and not self.connected:
for peer in self.peers:
if peer.inviteeDevID and not peer == self:
if not peer.inviteeDevID in self.inviteeDevIDs:
self.inviteeDevIDs.append(peer.inviteeDevID)
if self.inviteeDevIDs:
args += [ '--force-invite' ]
for inviteeDevID in self.inviteeDevIDs:
args += ['--invitee-relayid', str(inviteeDevID)]
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
stderr = subprocess.PIPE, universal_newlines = True)
self.pid = self.proc.pid
@ -416,8 +442,7 @@ def build_cmds(args):
assert(len(LOCALS) == NDEVS)
DICT = args.DICTS[GAME % len(args.DICTS)]
# make one in three games public
PUBLIC = []
if random.randint(0, 3) == 0: PUBLIC = ['--make-public', '--join-public']
usePublic = random.randint(0, 3) == 0
useDupeMode = random.randint(0, 100) < args.DUP_PCT
DEV = 0
for NLOCALS in LOCALS:
@ -471,20 +496,18 @@ def build_cmds(args):
# passed in the old bash version of this script, so fixing
# it isn't a priority.
# PARAMS += ['--seed', args.SEED]
PARAMS += PUBLIC
if DEV > 1:
PARAMS += ['--force-channel', DEV - 1]
else:
PARAMS += ['--server']
PARAMS += [ '--force-game' ]
if DEV == 1: PARAMS += ['--server']
if DEV == 1 or usePublic: PARAMS += ['--force-game']
if DEV > 1: PARAMS += ['--force-channel', DEV - 1]
if useDupeMode: PARAMS += ['--duplicate-mode']
if usePublic: PARAMS += ['--make-public', '--join-public']
# print('PARAMS:', PARAMS)
if useDupeMode: PARAMS += ['--duplicate-mode']
dev = Device(args, GAME, COUNTER, PARAMS, ROOM, peers,
DB, LOG, SCRIPT, len(LOCALS), useDupeMode)
dev = Device( args, GAME, COUNTER, PARAMS, ROOM, peers,
DEV, DB, LOG, SCRIPT, len(LOCALS), useDupeMode, usePublic )
peers.add(dev)
dev.update_ldevid()
devs.append(dev)

View file

@ -60,7 +60,7 @@ if [ -n "$LIMIT" ]; then
fi
# Games
echo "SELECT dead as d,connname,cid,room,lang as lg,clntVers as cv ,ntotal as t,nperdevice as npd,nsents as snts, seeds,devids,tokens,ack, mtimes "\
echo "SELECT dead as d,connname,cid,room,ack,lang as lg,clntVers as cv ,ntotal as t,nperdevice as npd,nsents as snts, seeds,devids,tokens, mtimes "\
"FROM games $QUERY $ORDER $LIMIT;" \
| psql xwgames

View file

@ -915,9 +915,9 @@ processConnect( const uint8_t* bufp, int bufLen, const AddrInfo* addr )
uint8_t clientIndx = getClientIndex( &bufp, end, nPlayersT );
logf( XW_LOGINFO, "%s(): langCode=%d; nPlayersT=%d; "
logf( XW_LOGINFO, "%s(): cookie='%s', langCode=%d; nPlayersT=%d; "
"wantsPublic=%d; seed=%.4X; indx=%d",
__func__, langCode, nPlayersT, wantsPublic, seed, clientIndx );
__func__, cookie, langCode, nPlayersT, wantsPublic, seed, clientIndx );
/* Make sure second thread can't create new cref for same cookie
this one just handled.*/