From f1956ddcb48b2947ce66f2bb40504a3d772165ee Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 27 Feb 2012 07:57:54 -0800 Subject: [PATCH] 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. --- .../org/eehouse/android/xw4/BTService.java | 102 ++++++++++++------ .../org/eehouse/android/xw4/GameUtils.java | 27 +++-- 2 files changed, 89 insertions(+), 40 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java index e5846342b..2a8c79651 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java @@ -35,6 +35,7 @@ import java.io.DataOutputStream; import java.io.OutputStream; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; @@ -62,6 +63,7 @@ public class BTService extends Service { , MESSAGE_NOGAME , MESSAGE_RESEND , MESSAGE_FAILOUT + , MESSAGE_DROPPED }; public interface BTEventListener { @@ -79,6 +81,7 @@ public class BTService extends Service { private static final int SEND = 3; private static final int RADIO = 4; private static final int CLEAR = 5; + private static final int REMOVE = 6; private static final String CMD_STR = "CMD"; private static final String MSG_STR = "MSG"; @@ -110,6 +113,7 @@ public class BTService extends Service { MESG_ACCPT, MESG_DECL, MESG_GAMEGONE, + REMOVE_FOR, }; private class BTQueueElem { @@ -224,6 +228,13 @@ public class BTService extends Service { targetName, targetAddr, gameID ); 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 ) { @@ -307,6 +318,12 @@ public class BTService extends Service { stopSelf(); } break; + case REMOVE: + gameID = intent.getIntExtra( GAMEID_STR, -1 ); + if ( -1 != gameID ) { + m_sender.removeFor( gameID ); + } + break; default: Assert.fail(); } @@ -578,11 +595,13 @@ public class BTService extends Service { private class BTSenderThread extends Thread { private LinkedBlockingQueue m_queue; private HashMap > m_resends; + private HashSet m_deadGames; public BTSenderThread() { m_queue = new LinkedBlockingQueue(); m_resends = new HashMap >(); + m_deadGames = new HashSet(); } public void add( BTQueueElem elem ) @@ -590,6 +609,13 @@ public class BTService extends Service { m_queue.add( elem ); } + public void removeFor( int gameID ) + { + synchronized( m_deadGames ) { + m_deadGames.add( gameID ); + } + } + @Override public void run() { @@ -713,44 +739,58 @@ public class BTService extends Service { private boolean sendMsg( BTQueueElem elem ) { - boolean success = false; - BTEvent evt = BTEvent.MESSAGE_REFUSED; - 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 ); + boolean success; + synchronized( m_deadGames ) { + success = m_deadGames.contains( elem.m_gameID ); + } + BTEvent evt; + if ( success ) { + evt = BTEvent.MESSAGE_DROPPED; + DbgUtils.logf( "dropping message because game %X dead", + 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; - outStream.writeShort( len ); - outStream.write( elem.m_msg, 0, elem.m_msg.length ); + short len = (short)elem.m_msg.length; + outStream.writeShort( len ); + outStream.write( elem.m_msg, 0, elem.m_msg.length ); - outStream.flush(); - Thread killer = killSocketIn( socket ); + outStream.flush(); + Thread killer = killSocketIn( socket ); - DataInputStream inStream = - new DataInputStream( socket.getInputStream() ); - BTCmd reply = BTCmd.values()[inStream.readByte()]; - killer.interrupt(); - success = true; + DataInputStream inStream = + new DataInputStream( socket.getInputStream() ); + BTCmd reply = BTCmd.values()[inStream.readByte()]; + killer.interrupt(); + success = true; - switch ( reply ) { - case MESG_ACCPT: - evt = BTEvent.MESSAGE_ACCEPTED; - break; - case MESG_GAMEGONE: - evt = BTEvent.MESSAGE_NOGAME; - break; + switch ( reply ) { + case MESG_ACCPT: + evt = BTEvent.MESSAGE_ACCEPTED; + break; + case MESG_GAMEGONE: + evt = BTEvent.MESSAGE_NOGAME; + 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 ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 10d9acbbf..04807491b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -223,7 +223,7 @@ public class GameUtils { public static void resetGame( Context context, long rowidIn ) { GameLock lock = new GameLock( rowidIn, true ).lock(); - tellRelayDied( context, lock, true ); + tellDied( context, lock, true ); resetGame( context, lock, lock, false ); lock.unlock(); } @@ -286,7 +286,7 @@ public class GameUtils { // does this need to be synchronized? GameLock lock = new GameLock( rowid, true ); if ( lock.tryLock() ) { - tellRelayDied( context, lock, informNow ); + tellDied( context, lock, informNow ); DBUtils.deleteGame( context, lock ); lock.unlock(); } @@ -757,7 +757,7 @@ public class GameUtils { CommonPrefs cp = CommonPrefs.get( context ); if ( forceNew ) { - tellRelayDied( context, lock, true ); + tellDied( context, lock, true ); } else { byte[] stream = savedGame( context, lock ); // Will fail if there's nothing in the stream but a gi. @@ -814,15 +814,24 @@ public class GameUtils { return rint; } - private static void tellRelayDied( Context context, GameLock lock, - boolean informNow ) + private static void tellDied( Context context, GameLock lock, + boolean informNow ) { GameSummary summary = DBUtils.getSummary( context, lock ); if ( null != summary.relayID ) { - DBUtils.addDeceased( context, summary.relayID, summary.seed ); - if ( informNow ) { - NetUtils.informOfDeaths( context ); - } + tellRelayDied( context, summary, informNow ); + } + 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 ); } }