snapshot of changes adding refcounting to jnithread so and instance

can be shared by multiple delegates, e.g. Board and Chat. Works but
with lots of crashes and stuff remaining to be done.
This commit is contained in:
Eric House 2016-03-31 22:05:58 -07:00
parent b6f992533a
commit f47a11aa42
12 changed files with 321 additions and 147 deletions

View file

@ -53,6 +53,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.LastMoveInfo; import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.loc.LocUtils;
import junit.framework.Assert; import junit.framework.Assert;
@ -591,9 +592,12 @@ public class BTService extends XWService {
boolean[] isLocalP = new boolean[1]; boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) { for ( long rowid : rowids ) {
boolean consumed = JNIThread jniThread = JNIThread.getRetained( rowid, false );
BoardDelegate.feedMessage( rowid, buffer, addr ); boolean consumed = false;
if ( !consumed && haveGame ) { if ( null != jniThread ) {
consumed = true;
jniThread.receive( buffer, addr ).release();
} else if ( haveGame ) {
GameUtils.BackMoveResult bmr = GameUtils.BackMoveResult bmr =
new GameUtils.BackMoveResult(); new GameUtils.BackMoveResult();
if ( GameUtils.feedMessage( BTService.this, rowid, if ( GameUtils.feedMessage( BTService.this, rowid,

View file

@ -128,6 +128,7 @@ public class BoardDelegate extends DelegateBase
private Thread m_blockingThread; private Thread m_blockingThread;
private JNIThread m_jniThread; private JNIThread m_jniThread;
private JNIThread m_jniThreadRef;
private JNIThread.GameStateInfo m_gsi; private JNIThread.GameStateInfo m_gsi;
private DlgID m_blockingDlgID = DlgID.NONE; private DlgID m_blockingDlgID = DlgID.NONE;
@ -142,55 +143,55 @@ public class BoardDelegate extends DelegateBase
private boolean m_haveInvited = false; private boolean m_haveInvited = false;
private boolean m_overNotShown; private boolean m_overNotShown;
private static Set<BoardDelegate> s_this = new HashSet<BoardDelegate>(); // private static Set<BoardDelegate> s_this = new HashSet<BoardDelegate>();
public static boolean feedMessage( long rowid, byte[] msg, // public static boolean feedMessage( long rowid, byte[] msg,
CommsAddrRec ret ) // CommsAddrRec ret )
{ // {
return feedMessages( rowid, new byte[][]{msg}, ret ); // return feedMessages( rowid, new byte[][]{msg}, ret );
} // }
public static boolean feedMessages( long rowid, byte[][] msgs, // public static boolean feedMessages( long rowid, byte[][] msgs,
CommsAddrRec ret ) // CommsAddrRec ret )
{ // {
boolean delivered = false; // boolean delivered = false;
Assert.assertNotNull( msgs ); // // Assert.assertNotNull( msgs );
int size; // // int size;
synchronized( s_this ) { // // synchronized( s_this ) {
size = s_this.size(); // // size = s_this.size();
if ( 1 == size ) { // // if ( 1 == size ) {
BoardDelegate self = s_this.iterator().next(); // // BoardDelegate self = s_this.iterator().next();
Assert.assertNotNull( self.m_gi ); // // Assert.assertNotNull( self.m_gi );
Assert.assertNotNull( self.m_gameLock ); // // Assert.assertNotNull( self.m_gameLock );
Assert.assertNotNull( self.m_jniThread ); // // Assert.assertNotNull( self.m_jniThread );
if ( rowid == self.m_rowid ) { // // if ( rowid == self.m_rowid ) {
delivered = true; // even if no messages! // // delivered = true; // even if no messages!
for ( byte[] msg : msgs ) { // // for ( byte[] msg : msgs ) {
self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, ret ); // // self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, ret );
} // // }
} // // }
} // // }
} // // }
if ( 1 < size ) { // // if ( 1 < size ) {
noteSkip(); // // noteSkip();
} // // }
return delivered; // return delivered;
} // }
private static void setThis( BoardDelegate self ) private static void setThis( BoardDelegate self )
{ {
synchronized( s_this ) { // synchronized( s_this ) {
Assert.assertTrue( !s_this.contains(self) ); // here // Assert.assertTrue( !s_this.contains(self) ); // here
s_this.add( self ); // s_this.add( self );
} // }
} }
private static void clearThis( BoardDelegate self ) private static void clearThis( BoardDelegate self )
{ {
synchronized( s_this ) { // synchronized( s_this ) {
Assert.assertTrue( s_this.contains( self ) ); // Assert.assertTrue( s_this.contains( self ) );
s_this.remove( self ); // s_this.remove( self );
} // }
} }
public class TimerRunnable implements Runnable { public class TimerRunnable implements Runnable {
@ -614,18 +615,14 @@ public class BoardDelegate extends DelegateBase
m_haveInvited = args.getBoolean( GameUtils.INVITED, false ); m_haveInvited = args.getBoolean( GameUtils.INVITED, false );
m_overNotShown = true; m_overNotShown = true;
m_jniThreadRef = JNIThread.getRetained( m_rowid, true );
NFCUtils.register( m_activity, this ); // Don't seem to need to unregister... NFCUtils.register( m_activity, this ); // Don't seem to need to unregister...
setBackgroundColor(); setBackgroundColor();
setKeepScreenOn(); setKeepScreenOn();
} // init } // init
protected void onPause()
{
closeIfFinishing( false );
super.onPause();
}
@Override @Override
protected void onStart() protected void onStart()
{ {
@ -640,21 +637,39 @@ public class BoardDelegate extends DelegateBase
doResume( false ); doResume( false );
} }
protected void onPause()
{
closeIfFinishing( false );
m_handler = null;
ConnStatusHandler.setHandler( null );
waitCloseGame( true );
pauseGame();
super.onPause();
}
@Override
protected void onStop()
{
if ( isFinishing() ) {
m_jniThreadRef.release();
m_jniThreadRef = null;
}
super.onStop();
}
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
closeIfFinishing( true ); closeIfFinishing( true );
GamesListDelegate.boardDestroyed( m_rowid ); if ( null != m_jniThreadRef ) {
m_jniThreadRef.release();
m_jniThreadRef = null;
// Assert.assertNull( m_jniThreadRef ); // firing
GamesListDelegate.boardDestroyed( m_rowid );
}
super.onDestroy(); super.onDestroy();
} }
// @Override
// protected void onDestroy()
// {
// GamesListDelegate.boardDestroyed( m_rowid );
// super.onDestroy();
// }
protected void onSaveInstanceState( Bundle outState ) protected void onSaveInstanceState( Bundle outState )
{ {
outState.putInt( DLG_TITLE, m_dlgTitle ); outState.putInt( DLG_TITLE, m_dlgTitle );
@ -2028,14 +2043,23 @@ public class BoardDelegate extends DelegateBase
if ( null == m_jniGamePtr ) { if ( null == m_jniGamePtr ) {
try { try {
String gameName = DBUtils.getName( m_activity, m_rowid ); String gameName = DBUtils.getName( m_activity, m_rowid );
String[] dictNames = GameUtils.dictNames( m_activity, m_rowid ); String[] dictNames;
Assert.assertNull( m_gameLock );
m_gameLock = m_jniThreadRef.getLock();
if ( null == m_gameLock ) {
dictNames = GameUtils.dictNames( m_activity, m_rowid );
} else {
dictNames = GameUtils.dictNames( m_activity, m_gameLock );
}
DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, dictNames ); DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, dictNames );
if ( pairs.anyMissing( dictNames ) ) { if ( pairs.anyMissing( dictNames ) ) {
showDictGoneFinish(); showDictGoneFinish();
} else { } else {
Assert.assertNull( m_gameLock ); if ( null == m_gameLock ) {
m_gameLock = new GameLock( m_rowid, true ).lock(); m_gameLock = new GameLock( m_rowid, true ).lock();
}
byte[] stream = GameUtils.savedGame( m_activity, m_gameLock ); byte[] stream = GameUtils.savedGame( m_activity, m_gameLock );
m_gi = new CurGameInfo( m_activity ); m_gi = new CurGameInfo( m_activity );
@ -2110,13 +2134,13 @@ public class BoardDelegate extends DelegateBase
} }
} }
}; };
m_jniThread = m_jniThread = m_jniThreadRef
new JNIThread( m_jniGamePtr, stream, m_gi, .configure( m_jniGamePtr, stream, m_gi, m_view,
m_view, m_gameLock, m_activity, handler ); m_gameLock, m_activity, handler );
// see http://stackoverflow.com/questions/680180/where-to-stop-\ // see http://stackoverflow.com/questions/680180/where-to-stop-\
// destroy-threads-in-android-service-class // destroy-threads-in-android-service-class
m_jniThread.setDaemon( true ); m_jniThread.setDaemonOnce( true ); // firing
m_jniThread.start(); m_jniThread.startOnce();
m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi, m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi,
m_connTypes ); m_connTypes );
@ -2392,20 +2416,12 @@ public class BoardDelegate extends DelegateBase
} }
} }
private void waitCloseGame( boolean save ) private void pauseGame()
{ {
if ( null != m_jniGamePtr ) { if ( null != m_jniGamePtr ) {
if ( null != m_xport ) {
m_xport.waitToStop();
m_xport = null;
}
interruptBlockingThread(); interruptBlockingThread();
if ( null != m_jniThread ) { m_jniThread = null;
m_jniThread.waitToStop( save );
m_jniThread = null;
}
m_view.stopHandling(); m_view.stopHandling();
clearThis( this ); clearThis( this );
@ -2417,12 +2433,25 @@ public class BoardDelegate extends DelegateBase
GameUtils.takeSnapshot( m_activity, m_jniGamePtr, m_gi ); GameUtils.takeSnapshot( m_activity, m_jniGamePtr, m_gi );
DBUtils.saveThumbnail( m_activity, m_gameLock, thumb ); DBUtils.saveThumbnail( m_activity, m_gameLock, thumb );
} }
}
}
private void waitCloseGame( boolean save )
{
pauseGame();
if ( null != m_jniGamePtr ) {
if ( null != m_xport ) {
m_xport.waitToStop();
m_xport = null;
}
clearThis( this );
m_jniGamePtr.release(); m_jniGamePtr.release();
m_jniGamePtr = null; m_jniGamePtr = null;
m_gi = null; m_gi = null;
m_gameLock.unlock(); // m_gameLock.unlock(); // likely the problem
m_gameLock = null; m_gameLock = null;
} }
} }

View file

@ -307,15 +307,20 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
// SyncedDraw interface implementation // SyncedDraw interface implementation
public void doJNIDraw() public void doJNIDraw()
{ {
boolean drew; boolean drew = false;
synchronized( this ) { synchronized( this ) {
if ( !XwJNI.board_draw( m_jniGamePtr ) ) { if ( null != m_jniGamePtr ) {
DbgUtils.logf( "doJNIDraw: draw not complete" ); drew = XwJNI.board_draw( m_jniGamePtr );
if ( !drew ) {
DbgUtils.logf( "doJNIDraw: draw not complete" );
}
} }
} }
// Force update now that we have bits to copy // Force update now that we have bits to copy
m_parent.runOnUiThread( m_invalidator ); if ( drew ) {
m_parent.runOnUiThread( m_invalidator );
}
} }
public void dimsChanged( BoardDims dims ) public void dimsChanged( BoardDims dims )

View file

@ -54,6 +54,7 @@ public class ChatDelegate extends DelegateBase {
private EditText m_edit; private EditText m_edit;
private TableLayout m_layout; private TableLayout m_layout;
private ScrollView m_scroll; private ScrollView m_scroll;
private JNIThread m_jniThreadRef;
public ChatDelegate( Delegator delegator, Bundle savedInstanceState ) public ChatDelegate( Delegator delegator, Bundle savedInstanceState )
{ {
@ -112,6 +113,21 @@ public class ChatDelegate extends DelegateBase {
} }
} }
@Override
protected void onResume()
{
m_jniThreadRef = JNIThread.getRetained( m_rowid );
Assert.assertNotNull( m_jniThreadRef );
super.onResume();
}
@Override
protected void onPause()
{
m_jniThreadRef.release();
super.onPause();
}
private void addRow( String msg, int playerIndx ) private void addRow( String msg, int playerIndx )
{ {
TableRow row = (TableRow)inflate( R.layout.chat_row ); TableRow row = (TableRow)inflate( R.layout.chat_row );
@ -167,15 +183,15 @@ public class ChatDelegate extends DelegateBase {
addRow( text, m_curPlayer ); addRow( text, m_curPlayer );
m_edit.setText( null ); m_edit.setText( null );
JNIThread jniThread = JNIThread.getCurrent(); m_jniThreadRef.sendChat( text );
if ( null != jniThread ) { // if ( null != jniThread ) {
jniThread.handle( JNIThread.JNICmd.CMD_SENDCHAT, text ); // jniThread.handle( JNIThread.JNICmd.CMD_SENDCHAT, text );
} else { // } else {
Intent result = new Intent(); // Intent result = new Intent();
result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text ); // result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text );
setResult( Activity.RESULT_OK, result ); // setResult( Activity.RESULT_OK, result );
finish(); // finish();
} // }
} }
// finish(); // finish();
break; break;

View file

@ -351,7 +351,7 @@ public class GameListItem extends LinearLayout
@Override @Override
protected GameSummary doInBackground( Void... unused ) protected GameSummary doInBackground( Void... unused )
{ {
return DBUtils.getSummary( m_context, m_rowid, 150 ); return DBUtils.getSummary( m_context, m_rowid, 500 );
} // doInBackground } // doInBackground
@Override @Override

View file

@ -28,7 +28,9 @@ import junit.framework.Assert;
// obtainable when other read locks are granted but not when a // obtainable when other read locks are granted but not when a
// write lock is. Write-locks are exclusive. // write lock is. Write-locks are exclusive.
public class GameLock { public class GameLock {
private static final int SLEEP_TIME = 25; private static final boolean DEBUG_LOCKS = true;
private static final int SLEEP_TIME = 100;
private static final long ASSERT_TIME = 2000;
private long m_rowid; private long m_rowid;
private boolean m_isForWrite; private boolean m_isForWrite;
private int m_lockCount; private int m_lockCount;
@ -43,7 +45,7 @@ public class GameLock {
m_rowid = rowid; m_rowid = rowid;
m_isForWrite = isForWrite; m_isForWrite = isForWrite;
m_lockCount = 0; m_lockCount = 0;
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>" DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>"
+ "this: %H", rowid, isForWrite, this ); + "this: %H", rowid, isForWrite, this );
DbgUtils.printStack(); DbgUtils.printStack();
@ -69,26 +71,26 @@ public class GameLock {
++m_lockCount; ++m_lockCount;
gotIt = true; gotIt = true;
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
StackTraceElement[] trace StackTraceElement[] trace
= Thread.currentThread().getStackTrace(); = Thread.currentThread().getStackTrace();
m_lockTrace = new StackTraceElement[trace.length]; m_lockTrace = new StackTraceElement[trace.length];
System.arraycopy( trace, 0, m_lockTrace, 0, trace.length ); System.arraycopy( trace, 0, m_lockTrace, 0, trace.length );
} }
} else if ( this == owner && ! m_isForWrite ) { } else if ( this == owner && ! m_isForWrite ) {
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "tryLock(): incrementing lock count" ); DbgUtils.logf( "tryLock(): incrementing lock count" );
} }
Assert.assertTrue( 0 == m_lockCount ); Assert.assertTrue( 0 == m_lockCount );
++m_lockCount; ++m_lockCount;
gotIt = true; gotIt = true;
owner = null; owner = null;
} else if ( XWApp.DEBUG_LOCKS ) { } else if ( DEBUG_LOCKS ) {
DbgUtils.logf( "tryLock(): rowid %d already held by lock %H", DbgUtils.logf( "tryLock(): rowid %d already held by lock %H",
m_rowid, owner ); m_rowid, owner );
} }
} }
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.tryLock %H (rowid=%d) => %b", DbgUtils.logf( "GameLock.tryLock %H (rowid=%d) => %b",
this, m_rowid, gotIt ); this, m_rowid, gotIt );
} }
@ -105,11 +107,10 @@ public class GameLock {
public GameLock lock( long maxMillis ) public GameLock lock( long maxMillis )
{ {
GameLock result = null; GameLock result = null;
final long assertTime = 2000; Assert.assertTrue( maxMillis < ASSERT_TIME );
Assert.assertTrue( maxMillis < assertTime );
long sleptTime = 0; long sleptTime = 0;
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid, DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid,
maxMillis ); maxMillis );
} }
@ -120,9 +121,9 @@ public class GameLock {
result = this; result = this;
break; break;
} }
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this ); DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this );
if ( 0 == sleptTime || sleptTime + SLEEP_TIME >= assertTime ) { if ( 0 == sleptTime || sleptTime + SLEEP_TIME >= ASSERT_TIME ) {
DbgUtils.logf( "lock %H seeking stack:", curOwner ); DbgUtils.logf( "lock %H seeking stack:", curOwner );
DbgUtils.printStack( curOwner.m_lockTrace ); DbgUtils.printStack( curOwner.m_lockTrace );
DbgUtils.logf( "lock %H seeking stack:", this ); DbgUtils.logf( "lock %H seeking stack:", this );
@ -137,18 +138,18 @@ public class GameLock {
break; break;
} }
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.lock() %H awake; " DbgUtils.logf( "GameLock.lock() %H awake; "
+ "sleptTime now %d millis", this, sleptTime ); + "sleptTime now %d millis", this, sleptTime );
} }
if ( 0 < maxMillis && sleptTime >= maxMillis ) { if ( 0 < maxMillis && sleptTime >= maxMillis ) {
break; break;
} else if ( sleptTime >= assertTime ) { } else if ( sleptTime >= ASSERT_TIME ) {
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "lock %H overlocked", this ); DbgUtils.logf( "lock %H overlocked", this );
} }
Assert.fail(); Assert.fail(); // firing
} }
} }
// DbgUtils.logf( "GameLock.lock(%s) done", m_path ); // DbgUtils.logf( "GameLock.lock(%s) done", m_path );
@ -167,7 +168,7 @@ public class GameLock {
} }
--m_lockCount; --m_lockCount;
if ( XWApp.DEBUG_LOCKS ) { if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) unlocked", DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) unlocked",
this, m_rowid ); this, m_rowid );
} }

View file

@ -369,6 +369,7 @@ public class GameUtils {
ThumbCanvas canvas = new ThumbCanvas( context, thumb ); ThumbCanvas canvas = new ThumbCanvas( context, thumb );
XwJNI.board_setDraw( gamePtr, canvas ); XwJNI.board_setDraw( gamePtr, canvas );
XwJNI.board_invalAll( gamePtr ); XwJNI.board_invalAll( gamePtr );
Assert.assertNotNull( gamePtr );
XwJNI.board_draw( gamePtr ); XwJNI.board_draw( gamePtr );
} }
} }
@ -746,6 +747,15 @@ public class GameUtils {
// } // }
// } // }
public static String[] dictNames( Context context, GameLock lock )
{
byte[] stream = savedGame( context, lock );
CurGameInfo gi = new CurGameInfo( context );
XwJNI.gi_from_stream( gi, stream );
return gi.dictNames();
}
public static String[] dictNames( Context context, long rowid, public static String[] dictNames( Context context, long rowid,
int[] missingLang ) int[] missingLang )
{ {

View file

@ -54,6 +54,7 @@ import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.LastMoveInfo; import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType; import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.loc.LocUtils;
public class RelayService extends XWService public class RelayService extends XWService
@ -999,9 +1000,10 @@ public class RelayService extends XWService
{ {
DbgUtils.logdf( "RelayService::feedMessage: %d bytes for rowid %d", DbgUtils.logdf( "RelayService::feedMessage: %d bytes for rowid %d",
msg.length, rowid ); msg.length, rowid );
if ( BoardDelegate.feedMessage( rowid, msg, s_addr ) ) { JNIThread jniThread = JNIThread.getRetained( rowid, false );
if ( null != jniThread ) {
jniThread.receive( msg, s_addr ).release();
DbgUtils.logdf( "feedMessage: board ate it" ); DbgUtils.logdf( "feedMessage: board ate it" );
// do nothing
} else { } else {
RelayMsgSink sink = new RelayMsgSink(); RelayMsgSink sink = new RelayMsgSink();
sink.setRowID( rowid ); sink.setRowID( rowid );
@ -1043,19 +1045,31 @@ public class RelayService extends XWService
// if game has messages, open it and feed 'em to it. // if game has messages, open it and feed 'em to it.
if ( null != forOne ) { if ( null != forOne ) {
BackMoveResult bmr = new BackMoveResult(); BackMoveResult bmr = new BackMoveResult();
sink.setRowID( rowIDs[ii] ); long rowid = rowIDs[ii];
sink.setRowID( rowid );
// since BoardDelegate.feedMessages can't know: // since BoardDelegate.feedMessages can't know:
isLocalP[0] = false; isLocalP[0] = false;
if ( BoardDelegate.feedMessages( rowIDs[ii], forOne, s_addr ) boolean delivered = true;
|| GameUtils.feedMessages( this, rowIDs[ii],
forOne, s_addr, JNIThread jniThread = JNIThread.getRetained( rowid, false );
sink, bmr, isLocalP ) ) { if ( null != jniThread ) {
for ( byte[] msg : forOne ) {
jniThread.receive( msg, s_addr );
}
jniThread.release();
} else if ( GameUtils.feedMessages( this, rowid, forOne, s_addr,
sink, bmr, isLocalP ) ) {
} else {
delivered = false;
}
if ( delivered ) {
idsWMsgs.add( relayIDs[ii] ); idsWMsgs.add( relayIDs[ii] );
bmrs.add( bmr ); bmrs.add( bmr );
isLocals.add( isLocalP[0] ); isLocals.add( isLocalP[0] );
} else { } else {
DbgUtils.logf( "RelayService.process(): message for %s (rowid %d)" DbgUtils.logf( "RelayService.process(): message for %s (rowid %d)"
+ " not consumed", relayIDs[ii], rowIDs[ii] ); + " not consumed", relayIDs[ii], rowid );
} }
} }
} }

View file

@ -62,6 +62,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.LastMoveInfo; import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.loc.LocUtils;
public class SMSService extends XWService { public class SMSService extends XWService {
@ -709,8 +710,9 @@ public class SMSService extends XWService {
} else { } else {
boolean[] isLocalP = new boolean[1]; boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) { for ( long rowid : rowids ) {
if ( BoardDelegate.feedMessage( rowid, msg, addr ) ) { JNIThread jniThread = JNIThread.getRetained( rowid, false );
// do nothing if ( null != jniThread ) {
jniThread.receive( msg, addr ).release();
} else { } else {
SMSMsgSink sink = new SMSMsgSink( this ); SMSMsgSink sink = new SMSMsgSink( this );
BackMoveResult bmr = new BackMoveResult(); BackMoveResult bmr = new BackMoveResult();

View file

@ -38,8 +38,7 @@ public class XWApp extends Application {
public static final boolean REMATCH_SUPPORTED = true; public static final boolean REMATCH_SUPPORTED = true;
public static final boolean RELAYINVITE_SUPPORTED = false; public static final boolean RELAYINVITE_SUPPORTED = false;
public static final boolean ATTACH_SUPPORTED = false; public static final boolean ATTACH_SUPPORTED = false;
public static final boolean DEBUG_LOCKS = false; public static final boolean LOG_LIFECYLE = true;
public static final boolean LOG_LIFECYLE = false;
public static final boolean DEBUG_EXP_TIMERS = false; public static final boolean DEBUG_EXP_TIMERS = false;
public static final boolean GCM_IGNORED = false; public static final boolean GCM_IGNORED = false;
public static final boolean UDP_ENABLED = true; public static final boolean UDP_ENABLED = true;

View file

@ -30,6 +30,8 @@ import android.os.Message;
import java.lang.InterruptedException; import java.lang.InterruptedException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -160,6 +162,8 @@ public class JNIThread extends Thread {
private static final int kMinDivWidth = 10; private static final int kMinDivWidth = 10;
private int m_connsIconID = 0; private int m_connsIconID = 0;
private String m_newDict = null; private String m_newDict = null;
private long m_rowid;
private int m_refCount;
private LinkedBlockingQueue<QueueElem> m_queue; private LinkedBlockingQueue<QueueElem> m_queue;
@ -173,27 +177,35 @@ public class JNIThread extends Thread {
Object[] m_args; Object[] m_args;
} }
public JNIThread( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi, private JNIThread( long rowid )
SyncedDraw drawer, GameLock lock, Context context,
Handler handler )
{ {
synchronized( s_curThreads ) { m_rowid = rowid;
s_curThreads.add( this ); m_queue = new LinkedBlockingQueue<QueueElem>();
// DbgUtils.logf( "JNIThread(): added %H; now %d threads", this, s_curThreads.size() ); }
public JNIThread configure( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi,
SyncedDraw drawer, GameLock lock, Context context,
Handler handler )
{
if ( null != m_jniGamePtr ) {
m_jniGamePtr.release();
} }
m_jniGamePtr = gamePtr.retain();
m_jniGamePtr = gamePtr;
m_gameAtStart = gameAtStart; m_gameAtStart = gameAtStart;
m_gi = gi; m_gi = gi;
m_drawer = drawer; m_drawer = drawer;
m_lock = lock; Assert.assertTrue( lock.canWrite() );
m_lock = lock; // I own this now!
Assert.assertTrue( lock.canWrite() );
m_context = context; m_context = context;
m_handler = handler; m_handler = handler;
m_queue = new LinkedBlockingQueue<QueueElem>(); return this;
} }
public void waitToStop( boolean save ) public GameLock getLock() { return m_lock; }
private void waitToStop( boolean save )
{ {
synchronized ( this ) { synchronized ( this ) {
m_stopped = true; m_stopped = true;
@ -210,12 +222,9 @@ public class JNIThread extends Thread {
} catch ( java.lang.InterruptedException ie ) { } catch ( java.lang.InterruptedException ie ) {
DbgUtils.loge( ie ); DbgUtils.loge( ie );
} }
// m_jniGamePtr.release();
synchronized( s_curThreads ) { // m_jniGamePtr = null;
Assert.assertTrue( s_curThreads.contains( this ) ); m_lock.unlock();
s_curThreads.remove( this );
// DbgUtils.logf( "waitToStop: removed %H; now %d threads", this, s_curThreads.size() );
}
} }
public boolean busy() public boolean busy()
@ -340,6 +349,22 @@ public class JNIThread extends Thread {
} }
} }
boolean m_running = false;
public void startOnce()
{
if ( !m_running ) {
m_running = true;
start();
}
}
public void setDaemonOnce( boolean val )
{
if ( !m_running ) {
setDaemon( val );
}
}
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
public void run() public void run()
{ {
@ -637,7 +662,9 @@ public class JNIThread extends Thread {
} else { } else {
DbgUtils.logf( "JNIThread.run(): exiting without saving" ); DbgUtils.logf( "JNIThread.run(): exiting without saving" );
} }
XwJNI.threadDone(); m_jniGamePtr.release();
m_jniGamePtr = null;
// XwJNI.threadDone();
} // run } // run
public void handleBkgrnd( JNICmd cmd, Object... args ) public void handleBkgrnd( JNICmd cmd, Object... args )
@ -647,6 +674,17 @@ public class JNIThread extends Thread {
m_queue.add( elem ); m_queue.add( elem );
} }
public JNIThread receive( byte[] msg, CommsAddrRec addr )
{
handle( JNICmd.CMD_RECEIVE, msg, addr );
return this;
}
public void sendChat( String chat )
{
handle( JNICmd.CMD_SENDCHAT, chat );
}
public void handle( JNICmd cmd, Object... args ) public void handle( JNICmd cmd, Object... args )
{ {
QueueElem elem = new QueueElem( cmd, true, args ); QueueElem elem = new QueueElem( cmd, true, args );
@ -667,10 +705,54 @@ public class JNIThread extends Thread {
return XwJNI.server_do( gamePtr ); return XwJNI.server_do( gamePtr );
} }
// public void run( boolean isUI, Runnable runnable ) private static Map<Long, JNIThread> s_instances = new HashMap<Long, JNIThread>();
// { private void retain_sync()
// Object[] args = { runnable }; {
// QueueElem elem = new QueueElem( JNICmd.CMD_RUN, isUI, args ); ++m_refCount;
// m_queue.add( elem ); DbgUtils.logf( "JNIThread.retain_sync: m_refCount: %d", m_refCount );
// } }
public void retain()
{
synchronized( s_instances ) {
retain_sync();
}
}
public void release()
{
boolean stop = false;
synchronized( s_instances ) {
if ( 0 == --m_refCount ) {
s_instances.remove( m_rowid );
stop = true;
}
}
DbgUtils.logf( "JNIThread.release: m_refCount: %d", m_refCount );
if ( stop ) {
waitToStop( true );
}
}
public static JNIThread getRetained( long rowid )
{
return getRetained( rowid, false );
}
public static JNIThread getRetained( long rowid, boolean makeNew )
{
JNIThread result = null;
synchronized( s_instances ) {
result = s_instances.get( rowid );
if ( null == result && makeNew ) {
result = new JNIThread( rowid );
s_instances.put( rowid, result );
}
if ( null != result ) {
result.retain_sync();
}
}
return result;
}
} }

View file

@ -34,18 +34,30 @@ public class XwJNI {
public static class GamePtr { public static class GamePtr {
private int m_ptr = 0; private int m_ptr = 0;
private int m_refCount = 0;
private GamePtr( int ptr ) { m_ptr = ptr; } private GamePtr( int ptr ) {
m_ptr = ptr;
retain();
}
public int ptr() { Assert.assertTrue( 0 != m_ptr ); return m_ptr; } public int ptr() { Assert.assertTrue( 0 != m_ptr ); return m_ptr; }
public synchronized GamePtr retain()
{
++m_refCount;
return this;
}
// Force (via an assert in finalize() below) that this is called. It's // Force (via an assert in finalize() below) that this is called. It's
// better if jni stuff isn't being done on the finalizer thread // better if jni stuff isn't being done on the finalizer thread
public void release() public synchronized void release()
{ {
if ( 0 != m_ptr ) { if ( 0 == --m_refCount ) {
game_dispose( this ); if ( 0 != m_ptr ) {
m_ptr = 0; game_dispose( this );
m_ptr = 0;
}
} }
} }