mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-01 06:19:57 +01:00
Fix hangs when receiving relay messages in background for open game by
adding a static feedMessages method like the one used by SMS and BT games. For that to work, rowid and relayid need to be fetched and tracked together -- so do that in RelayService.
This commit is contained in:
parent
91ac04b896
commit
6060d5e8bd
7 changed files with 129 additions and 91 deletions
|
@ -189,6 +189,27 @@ public class BoardActivity extends XWActivity
|
|||
return delivered;
|
||||
}
|
||||
|
||||
public static boolean feedMessages( long rowid, byte[][] msgs )
|
||||
{
|
||||
boolean delivered = false;
|
||||
Assert.assertNotNull( msgs );
|
||||
synchronized( s_thisLocker ) {
|
||||
if ( null != s_this ) {
|
||||
Assert.assertNotNull( s_this.m_gi );
|
||||
Assert.assertNotNull( s_this.m_gameLock );
|
||||
Assert.assertNotNull( s_this.m_jniThread );
|
||||
if ( rowid == s_this.m_rowid ) {
|
||||
delivered = true; // even if no messages!
|
||||
for ( byte[] msg : msgs ) {
|
||||
s_this.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg,
|
||||
null );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return delivered;
|
||||
}
|
||||
|
||||
private static void setThis( BoardActivity self )
|
||||
{
|
||||
synchronized( s_thisLocker ) {
|
||||
|
@ -509,6 +530,7 @@ public class BoardActivity extends XWActivity
|
|||
|
||||
Intent intent = getIntent();
|
||||
m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 );
|
||||
DbgUtils.logf( "BoardActivity: opening rowid %d", m_rowid );
|
||||
m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false );
|
||||
m_overNotShown = true;
|
||||
|
||||
|
|
|
@ -573,7 +573,7 @@ public class DBUtils {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static String[] getRelayIDs( Context context, boolean noMsgs )
|
||||
public static String[] getRelayIDs( Context context, long[][] rowIDs )
|
||||
{
|
||||
String[] result = null;
|
||||
initDB( context );
|
||||
|
@ -581,26 +581,31 @@ public class DBUtils {
|
|||
|
||||
synchronized( s_dbHelper ) {
|
||||
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
|
||||
String[] columns = { DBHelper.RELAYID };
|
||||
String[] columns = { ROW_ID, DBHelper.RELAYID };
|
||||
String selection = DBHelper.RELAYID + " NOT null";
|
||||
if ( noMsgs ) {
|
||||
selection += " AND NOT " + DBHelper.HASMSGS;
|
||||
}
|
||||
|
||||
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
|
||||
selection, null, null, null, null );
|
||||
int count = cursor.getCount();
|
||||
if ( 0 < count ) {
|
||||
result = new String[count];
|
||||
if ( null != rowIDs ) {
|
||||
rowIDs[0] = new long[count];
|
||||
}
|
||||
|
||||
while ( cursor.moveToNext() ) {
|
||||
ids.add( cursor.getString( cursor.
|
||||
getColumnIndex(DBHelper.RELAYID)) );
|
||||
int idIndex = cursor.getColumnIndex(DBHelper.RELAYID);
|
||||
int rowIndex = cursor.getColumnIndex(ROW_ID);
|
||||
for ( int ii = 0; cursor.moveToNext(); ++ii ) {
|
||||
result[ii] = cursor.getString( idIndex );
|
||||
if ( null != rowIDs ) {
|
||||
rowIDs[0][ii] = cursor.getLong( rowIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
db.close();
|
||||
}
|
||||
|
||||
if ( 0 < ids.size() ) {
|
||||
result = ids.toArray( new String[ids.size()] );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ public class DlgDelegate {
|
|||
|
||||
public void doSyncMenuitem()
|
||||
{
|
||||
if ( null == DBUtils.getRelayIDs( m_activity, false ) ) {
|
||||
if ( null == DBUtils.getRelayIDs( m_activity, null ) ) {
|
||||
showOKOnlyDialog( R.string.no_games_to_refresh );
|
||||
} else {
|
||||
RelayReceiver.RestartTimer( m_activity, true );
|
||||
|
|
|
@ -111,16 +111,21 @@ public class GameUtils {
|
|||
final long assertTime = 2000;
|
||||
Assert.assertTrue( maxMillis < assertTime );
|
||||
long sleptTime = 0;
|
||||
// DbgUtils.logf( "GameLock.lock(%s)", m_path );
|
||||
// Utils.printStack();
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)",
|
||||
this, m_rowid, maxMillis );
|
||||
}
|
||||
|
||||
for ( ; ; ) {
|
||||
if ( tryLock() ) {
|
||||
result = this;
|
||||
break;
|
||||
}
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this );
|
||||
DbgUtils.printStack();
|
||||
DbgUtils.logf( "GameLock.lock() %H failed (rowid:%d); sleeping",
|
||||
this, m_rowid );
|
||||
// DbgUtils.printStack();
|
||||
}
|
||||
try {
|
||||
Thread.sleep( 25 ); // milliseconds
|
||||
|
@ -130,12 +135,17 @@ public class GameUtils {
|
|||
break;
|
||||
}
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.lock() %H awake; "
|
||||
+ "sleptTime now %d millis", this, sleptTime );
|
||||
}
|
||||
|
||||
if ( 0 < maxMillis && sleptTime >= maxMillis ) {
|
||||
break;
|
||||
} else if ( sleptTime >= assertTime ) {
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "lock %H overlocked. lock holding stack:",
|
||||
this );
|
||||
DbgUtils.logf( "lock %H overlocked waiting for rowid:%d. "
|
||||
+ "lock holding stack:", m_rowid, this );
|
||||
DbgUtils.printStack( m_lockTrace );
|
||||
DbgUtils.logf( "lock %H seeking stack:", this );
|
||||
DbgUtils.printStack();
|
||||
|
@ -158,8 +168,12 @@ public class GameUtils {
|
|||
Assert.assertTrue( !m_isForWrite );
|
||||
}
|
||||
--m_lockCount;
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) "
|
||||
+ "unlocked", this, m_rowid );
|
||||
}
|
||||
}
|
||||
// DbgUtils.logf( "GameLock.unlock(%s) done", m_path );
|
||||
}
|
||||
|
||||
public long getRowid()
|
||||
|
@ -707,41 +721,46 @@ public class GameUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean feedMessages( Context context, long rowid,
|
||||
byte[][] msgs, CommsAddrRec ret,
|
||||
MultiMsgSink sink )
|
||||
public static boolean feedMessages( Context context, long rowid,
|
||||
byte[][] msgs, CommsAddrRec ret,
|
||||
MultiMsgSink sink )
|
||||
{
|
||||
boolean draw = false;
|
||||
Assert.assertTrue( -1 != rowid );
|
||||
GameLock lock = new GameLock( rowid, true ).lock();
|
||||
if ( null != msgs ) {
|
||||
// timed lock: If a game is opened by BoardActivity just
|
||||
// as we're trying to deliver this message to it it'll
|
||||
// have the lock and we'll never get it. Better to drop
|
||||
// the message than fire the hung-lock assert. Messages
|
||||
// belong in local pre-delivery storage anyway.
|
||||
GameLock lock = new GameLock( rowid, true ).lock( 150 );
|
||||
if ( null != lock ) {
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
|
||||
int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
|
||||
if ( 0 != gamePtr ) {
|
||||
XwJNI.comms_resendAll( gamePtr, false, false );
|
||||
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
|
||||
int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
|
||||
if ( 0 != gamePtr ) {
|
||||
XwJNI.comms_resendAll( gamePtr, false, false );
|
||||
for ( byte[] msg : msgs ) {
|
||||
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret )
|
||||
|| draw;
|
||||
}
|
||||
XwJNI.comms_ackAny( gamePtr );
|
||||
|
||||
if ( null != msgs ) {
|
||||
for ( byte[] msg : msgs ) {
|
||||
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret )
|
||||
|| draw;
|
||||
// update gi to reflect changes due to messages
|
||||
XwJNI.game_getGi( gamePtr, gi );
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
summarizeAndClose( context, lock, gamePtr, gi, feedImpl );
|
||||
|
||||
int flags = setFromFeedImpl( feedImpl );
|
||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||
draw = true;
|
||||
DBUtils.setMsgFlags( rowid, flags );
|
||||
}
|
||||
}
|
||||
}
|
||||
XwJNI.comms_ackAny( gamePtr );
|
||||
|
||||
// update gi to reflect changes due to messages
|
||||
XwJNI.game_getGi( gamePtr, gi );
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
summarizeAndClose( context, lock, gamePtr, gi, feedImpl );
|
||||
|
||||
int flags = setFromFeedImpl( feedImpl );
|
||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||
draw = true;
|
||||
DBUtils.setMsgFlags( rowid, flags );
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
return draw;
|
||||
} // feedMessages
|
||||
|
||||
|
@ -754,21 +773,6 @@ public class GameUtils {
|
|||
return feedMessages( context, rowid, msgs, ret, sink );
|
||||
}
|
||||
|
||||
// Current assumption: this is the relay case where return address
|
||||
// can be null.
|
||||
public static boolean feedMessages( Context context, String relayID,
|
||||
byte[][] msgs, MultiMsgSink sink )
|
||||
{
|
||||
boolean draw = false;
|
||||
long[] rowids = DBUtils.getRowIDsFor( context, relayID );
|
||||
if ( null != rowids ) {
|
||||
for ( long rowid : rowids ) {
|
||||
draw = feedMessages( context, rowid, msgs, null, sink ) || draw;
|
||||
}
|
||||
}
|
||||
return draw;
|
||||
}
|
||||
|
||||
// This *must* involve a reset if the language is changing!!!
|
||||
// Which isn't possible right now, so make sure the old and new
|
||||
// dict have the same langauge code.
|
||||
|
|
|
@ -130,8 +130,7 @@ public class NetUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[][][] queryRelay( Context context, String[] ids,
|
||||
int nBytes )
|
||||
public static byte[][][] queryRelay( Context context, String[] ids )
|
||||
{
|
||||
byte[][][] msgs = null;
|
||||
try {
|
||||
|
@ -141,6 +140,7 @@ public class NetUtils {
|
|||
new DataOutputStream( socket.getOutputStream() );
|
||||
|
||||
// total packet size
|
||||
int nBytes = sumStrings( ids );
|
||||
outStream.writeShort( 2 + nBytes + ids.length + 1 );
|
||||
|
||||
outStream.writeByte( NetUtils.PROTOCOL_VERSION );
|
||||
|
@ -263,4 +263,16 @@ public class NetUtils {
|
|||
DbgUtils.logf( "sendToRelay: null msgs" );
|
||||
}
|
||||
} // sendToRelay
|
||||
|
||||
private static int sumStrings( final String[] strs )
|
||||
{
|
||||
int len = 0;
|
||||
if ( null != strs ) {
|
||||
for ( String str : strs ) {
|
||||
len += str.length();
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,45 +74,40 @@ public class RelayService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] collectIDs( int[] nBytes )
|
||||
{
|
||||
String[] ids = DBUtils.getRelayIDs( this, false );
|
||||
int len = 0;
|
||||
if ( null != ids ) {
|
||||
for ( String id : ids ) {
|
||||
len += id.length();
|
||||
}
|
||||
}
|
||||
nBytes[0] = len;
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void fetchAndProcess()
|
||||
{
|
||||
int[] nBytes = new int[1];
|
||||
String[] ids = collectIDs( nBytes );
|
||||
if ( null != ids && 0 < ids.length ) {
|
||||
RelayMsgSink sink = new RelayMsgSink();
|
||||
byte[][][] msgs =
|
||||
NetUtils.queryRelay( this, ids, nBytes[0] );
|
||||
long[][] rowIDss = new long[1][];
|
||||
String[] relayIDs = DBUtils.getRelayIDs( this, rowIDss );
|
||||
if ( null != relayIDs && 0 < relayIDs.length ) {
|
||||
long[] rowIDs = rowIDss[0];
|
||||
byte[][][] msgs = NetUtils.queryRelay( this, relayIDs );
|
||||
|
||||
if ( null != msgs ) {
|
||||
int nameCount = ids.length;
|
||||
RelayMsgSink sink = new RelayMsgSink();
|
||||
int nameCount = relayIDs.length;
|
||||
ArrayList<String> idsWMsgs =
|
||||
new ArrayList<String>( nameCount );
|
||||
for ( int ii = 0; ii < nameCount; ++ii ) {
|
||||
byte[][] forOne = msgs[ii];
|
||||
// if game has messages, open it and feed 'em
|
||||
// to it.
|
||||
if ( GameUtils.feedMessages( this, ids[ii],
|
||||
msgs[ii], sink ) ) {
|
||||
idsWMsgs.add( ids[ii] );
|
||||
if ( null == forOne ) {
|
||||
// Nothing for this relayID
|
||||
} else if ( BoardActivity.feedMessages( rowIDs[ii], forOne )
|
||||
|| GameUtils.feedMessages( this, rowIDs[ii],
|
||||
forOne, null,
|
||||
sink ) ) {
|
||||
idsWMsgs.add( relayIDs[ii] );
|
||||
} else {
|
||||
DbgUtils.logf( "dropping message for %s (rowid %d)",
|
||||
relayIDs[ii], rowIDs[ii] );
|
||||
}
|
||||
}
|
||||
if ( 0 < idsWMsgs.size() ) {
|
||||
String[] relayIDs = new String[idsWMsgs.size()];
|
||||
idsWMsgs.toArray( relayIDs );
|
||||
setupNotification( relayIDs );
|
||||
String[] tmp = new String[idsWMsgs.size()];
|
||||
idsWMsgs.toArray( tmp );
|
||||
setupNotification( tmp );
|
||||
}
|
||||
sink.send( this );
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ import java.util.UUID;
|
|||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
|
||||
public class XWApp extends Application {
|
||||
public static final boolean DEBUG_LOCKS = false;
|
||||
public static final boolean BTSUPPORTED = false;
|
||||
public static final boolean SMSSUPPORTED = true;
|
||||
public static final boolean GCMSUPPORTED = true;
|
||||
public static final boolean ATTACH_SUPPORTED = true;
|
||||
public static final boolean REMATCH_SUPPORTED = false;
|
||||
public static final boolean DEBUG = true; // DON'T SHIP THIS WAY
|
||||
public static final boolean DEBUG = true; // DON'T SHIP THIS WAY
|
||||
public static final boolean DEBUG_LOCKS = false && DEBUG;
|
||||
|
||||
public static final String SMS_PUBLIC_HEADER = "-XW4";
|
||||
|
||||
|
|
Loading…
Reference in a new issue