prevent continuing attempts to send messages from client after it's

deleted the game those messages are part of.  I'm keeping a list of
dead games and checking against that, and currently that list will
grow for as long as the sender thread lives.  Shouldn't be a problem
but needs fixing.  Better would be to remove all messages from queues,
but that has synchronization issues.
This commit is contained in:
Eric House 2012-02-27 07:57:54 -08:00
parent 456dcca7b8
commit f1956ddcb4
2 changed files with 89 additions and 40 deletions

View file

@ -35,6 +35,7 @@ import java.io.DataOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.ListIterator; import java.util.ListIterator;
@ -62,6 +63,7 @@ public class BTService extends Service {
, MESSAGE_NOGAME , MESSAGE_NOGAME
, MESSAGE_RESEND , MESSAGE_RESEND
, MESSAGE_FAILOUT , MESSAGE_FAILOUT
, MESSAGE_DROPPED
}; };
public interface BTEventListener { public interface BTEventListener {
@ -79,6 +81,7 @@ public class BTService extends Service {
private static final int SEND = 3; private static final int SEND = 3;
private static final int RADIO = 4; private static final int RADIO = 4;
private static final int CLEAR = 5; private static final int CLEAR = 5;
private static final int REMOVE = 6;
private static final String CMD_STR = "CMD"; private static final String CMD_STR = "CMD";
private static final String MSG_STR = "MSG"; private static final String MSG_STR = "MSG";
@ -110,6 +113,7 @@ public class BTService extends Service {
MESG_ACCPT, MESG_ACCPT,
MESG_DECL, MESG_DECL,
MESG_GAMEGONE, MESG_GAMEGONE,
REMOVE_FOR,
}; };
private class BTQueueElem { private class BTQueueElem {
@ -224,6 +228,13 @@ public class BTService extends Service {
targetName, targetAddr, gameID ); targetName, targetAddr, gameID );
return buf.length; return buf.length;
} }
public static void gameDied( Context context, int gameID )
{
Intent intent = getIntentTo( context, REMOVE );
intent.putExtra( GAMEID_STR, gameID );
context.startService( intent );
}
private static Intent getIntentTo( Context context, int cmd ) private static Intent getIntentTo( Context context, int cmd )
{ {
@ -307,6 +318,12 @@ public class BTService extends Service {
stopSelf(); stopSelf();
} }
break; break;
case REMOVE:
gameID = intent.getIntExtra( GAMEID_STR, -1 );
if ( -1 != gameID ) {
m_sender.removeFor( gameID );
}
break;
default: default:
Assert.fail(); Assert.fail();
} }
@ -578,11 +595,13 @@ public class BTService extends Service {
private class BTSenderThread extends Thread { private class BTSenderThread extends Thread {
private LinkedBlockingQueue<BTQueueElem> m_queue; private LinkedBlockingQueue<BTQueueElem> m_queue;
private HashMap<String,LinkedList<BTQueueElem> > m_resends; private HashMap<String,LinkedList<BTQueueElem> > m_resends;
private HashSet<Integer> m_deadGames;
public BTSenderThread() public BTSenderThread()
{ {
m_queue = new LinkedBlockingQueue<BTQueueElem>(); m_queue = new LinkedBlockingQueue<BTQueueElem>();
m_resends = new HashMap<String,LinkedList<BTQueueElem> >(); m_resends = new HashMap<String,LinkedList<BTQueueElem> >();
m_deadGames = new HashSet<Integer>();
} }
public void add( BTQueueElem elem ) public void add( BTQueueElem elem )
@ -590,6 +609,13 @@ public class BTService extends Service {
m_queue.add( elem ); m_queue.add( elem );
} }
public void removeFor( int gameID )
{
synchronized( m_deadGames ) {
m_deadGames.add( gameID );
}
}
@Override @Override
public void run() public void run()
{ {
@ -713,44 +739,58 @@ public class BTService extends Service {
private boolean sendMsg( BTQueueElem elem ) private boolean sendMsg( BTQueueElem elem )
{ {
boolean success = false; boolean success;
BTEvent evt = BTEvent.MESSAGE_REFUSED; synchronized( m_deadGames ) {
try { success = m_deadGames.contains( elem.m_gameID );
BluetoothDevice dev = m_adapter.getRemoteDevice( elem.m_addr ); }
BluetoothSocket socket = BTEvent evt;
dev.createRfcommSocketToServiceRecord( XWApp.getAppUUID() ); if ( success ) {
if ( null != socket ) { evt = BTEvent.MESSAGE_DROPPED;
DataOutputStream outStream = connect( socket, BTCmd.MESG_SEND ); DbgUtils.logf( "dropping message because game %X dead",
if ( null != outStream ) { elem.m_gameID );
outStream.writeInt( elem.m_gameID ); } else {
evt = BTEvent.MESSAGE_REFUSED;
}
if ( !success ) {
try {
BluetoothDevice dev =
m_adapter.getRemoteDevice( elem.m_addr );
BluetoothSocket socket = dev.
createRfcommSocketToServiceRecord( XWApp.getAppUUID() );
if ( null != socket ) {
DataOutputStream outStream =
connect( socket, BTCmd.MESG_SEND );
if ( null != outStream ) {
outStream.writeInt( elem.m_gameID );
short len = (short)elem.m_msg.length; short len = (short)elem.m_msg.length;
outStream.writeShort( len ); outStream.writeShort( len );
outStream.write( elem.m_msg, 0, elem.m_msg.length ); outStream.write( elem.m_msg, 0, elem.m_msg.length );
outStream.flush(); outStream.flush();
Thread killer = killSocketIn( socket ); Thread killer = killSocketIn( socket );
DataInputStream inStream = DataInputStream inStream =
new DataInputStream( socket.getInputStream() ); new DataInputStream( socket.getInputStream() );
BTCmd reply = BTCmd.values()[inStream.readByte()]; BTCmd reply = BTCmd.values()[inStream.readByte()];
killer.interrupt(); killer.interrupt();
success = true; success = true;
switch ( reply ) { switch ( reply ) {
case MESG_ACCPT: case MESG_ACCPT:
evt = BTEvent.MESSAGE_ACCEPTED; evt = BTEvent.MESSAGE_ACCEPTED;
break; break;
case MESG_GAMEGONE: case MESG_GAMEGONE:
evt = BTEvent.MESSAGE_NOGAME; evt = BTEvent.MESSAGE_NOGAME;
break; break;
}
} }
socket.close();
} }
socket.close(); } catch ( java.io.IOException ioe ) {
DbgUtils.logf( "sendMsg: ioe: %s", ioe.toString() );
success = false;
} }
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "sendMsg: ioe: %s", ioe.toString() );
success = false;
} }
sendResult( evt, elem.m_gameID, 0, elem.m_recipient ); sendResult( evt, elem.m_gameID, 0, elem.m_recipient );

View file

@ -223,7 +223,7 @@ public class GameUtils {
public static void resetGame( Context context, long rowidIn ) public static void resetGame( Context context, long rowidIn )
{ {
GameLock lock = new GameLock( rowidIn, true ).lock(); GameLock lock = new GameLock( rowidIn, true ).lock();
tellRelayDied( context, lock, true ); tellDied( context, lock, true );
resetGame( context, lock, lock, false ); resetGame( context, lock, lock, false );
lock.unlock(); lock.unlock();
} }
@ -286,7 +286,7 @@ public class GameUtils {
// does this need to be synchronized? // does this need to be synchronized?
GameLock lock = new GameLock( rowid, true ); GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
tellRelayDied( context, lock, informNow ); tellDied( context, lock, informNow );
DBUtils.deleteGame( context, lock ); DBUtils.deleteGame( context, lock );
lock.unlock(); lock.unlock();
} }
@ -757,7 +757,7 @@ public class GameUtils {
CommonPrefs cp = CommonPrefs.get( context ); CommonPrefs cp = CommonPrefs.get( context );
if ( forceNew ) { if ( forceNew ) {
tellRelayDied( context, lock, true ); tellDied( context, lock, true );
} else { } else {
byte[] stream = savedGame( context, lock ); byte[] stream = savedGame( context, lock );
// Will fail if there's nothing in the stream but a gi. // Will fail if there's nothing in the stream but a gi.
@ -814,15 +814,24 @@ public class GameUtils {
return rint; return rint;
} }
private static void tellRelayDied( Context context, GameLock lock, private static void tellDied( Context context, GameLock lock,
boolean informNow ) boolean informNow )
{ {
GameSummary summary = DBUtils.getSummary( context, lock ); GameSummary summary = DBUtils.getSummary( context, lock );
if ( null != summary.relayID ) { if ( null != summary.relayID ) {
DBUtils.addDeceased( context, summary.relayID, summary.seed ); tellRelayDied( context, summary, informNow );
if ( informNow ) { }
NetUtils.informOfDeaths( context ); if ( 0 != summary.gameID ) {
} BTService.gameDied( context, summary.gameID );
}
}
private static void tellRelayDied( Context context, GameSummary summary,
boolean informNow )
{
DBUtils.addDeceased( context, summary.relayID, summary.seed );
if ( informNow ) {
NetUtils.informOfDeaths( context );
} }
} }