mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-07 05:24:46 +01:00
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:
parent
b6f992533a
commit
f47a11aa42
12 changed files with 321 additions and 147 deletions
|
@ -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.LastMoveInfo;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
@ -591,9 +592,12 @@ public class BTService extends XWService {
|
|||
|
||||
boolean[] isLocalP = new boolean[1];
|
||||
for ( long rowid : rowids ) {
|
||||
boolean consumed =
|
||||
BoardDelegate.feedMessage( rowid, buffer, addr );
|
||||
if ( !consumed && haveGame ) {
|
||||
JNIThread jniThread = JNIThread.getRetained( rowid, false );
|
||||
boolean consumed = false;
|
||||
if ( null != jniThread ) {
|
||||
consumed = true;
|
||||
jniThread.receive( buffer, addr ).release();
|
||||
} else if ( haveGame ) {
|
||||
GameUtils.BackMoveResult bmr =
|
||||
new GameUtils.BackMoveResult();
|
||||
if ( GameUtils.feedMessage( BTService.this, rowid,
|
||||
|
|
|
@ -128,6 +128,7 @@ public class BoardDelegate extends DelegateBase
|
|||
|
||||
private Thread m_blockingThread;
|
||||
private JNIThread m_jniThread;
|
||||
private JNIThread m_jniThreadRef;
|
||||
private JNIThread.GameStateInfo m_gsi;
|
||||
private DlgID m_blockingDlgID = DlgID.NONE;
|
||||
|
||||
|
@ -142,55 +143,55 @@ public class BoardDelegate extends DelegateBase
|
|||
private boolean m_haveInvited = false;
|
||||
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,
|
||||
CommsAddrRec ret )
|
||||
{
|
||||
return feedMessages( rowid, new byte[][]{msg}, ret );
|
||||
}
|
||||
// public static boolean feedMessage( long rowid, byte[] msg,
|
||||
// CommsAddrRec ret )
|
||||
// {
|
||||
// return feedMessages( rowid, new byte[][]{msg}, ret );
|
||||
// }
|
||||
|
||||
public static boolean feedMessages( long rowid, byte[][] msgs,
|
||||
CommsAddrRec ret )
|
||||
{
|
||||
boolean delivered = false;
|
||||
Assert.assertNotNull( msgs );
|
||||
int size;
|
||||
synchronized( s_this ) {
|
||||
size = s_this.size();
|
||||
if ( 1 == size ) {
|
||||
BoardDelegate self = s_this.iterator().next();
|
||||
Assert.assertNotNull( self.m_gi );
|
||||
Assert.assertNotNull( self.m_gameLock );
|
||||
Assert.assertNotNull( self.m_jniThread );
|
||||
if ( rowid == self.m_rowid ) {
|
||||
delivered = true; // even if no messages!
|
||||
for ( byte[] msg : msgs ) {
|
||||
self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, ret );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( 1 < size ) {
|
||||
noteSkip();
|
||||
}
|
||||
return delivered;
|
||||
}
|
||||
// public static boolean feedMessages( long rowid, byte[][] msgs,
|
||||
// CommsAddrRec ret )
|
||||
// {
|
||||
// boolean delivered = false;
|
||||
// // Assert.assertNotNull( msgs );
|
||||
// // int size;
|
||||
// // synchronized( s_this ) {
|
||||
// // size = s_this.size();
|
||||
// // if ( 1 == size ) {
|
||||
// // BoardDelegate self = s_this.iterator().next();
|
||||
// // Assert.assertNotNull( self.m_gi );
|
||||
// // Assert.assertNotNull( self.m_gameLock );
|
||||
// // Assert.assertNotNull( self.m_jniThread );
|
||||
// // if ( rowid == self.m_rowid ) {
|
||||
// // delivered = true; // even if no messages!
|
||||
// // for ( byte[] msg : msgs ) {
|
||||
// // self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, ret );
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // if ( 1 < size ) {
|
||||
// // noteSkip();
|
||||
// // }
|
||||
// return delivered;
|
||||
// }
|
||||
|
||||
private static void setThis( BoardDelegate self )
|
||||
{
|
||||
synchronized( s_this ) {
|
||||
Assert.assertTrue( !s_this.contains(self) ); // here
|
||||
s_this.add( self );
|
||||
}
|
||||
// synchronized( s_this ) {
|
||||
// Assert.assertTrue( !s_this.contains(self) ); // here
|
||||
// s_this.add( self );
|
||||
// }
|
||||
}
|
||||
|
||||
private static void clearThis( BoardDelegate self )
|
||||
{
|
||||
synchronized( s_this ) {
|
||||
Assert.assertTrue( s_this.contains( self ) );
|
||||
s_this.remove( self );
|
||||
}
|
||||
// synchronized( s_this ) {
|
||||
// Assert.assertTrue( s_this.contains( self ) );
|
||||
// s_this.remove( self );
|
||||
// }
|
||||
}
|
||||
|
||||
public class TimerRunnable implements Runnable {
|
||||
|
@ -614,18 +615,14 @@ public class BoardDelegate extends DelegateBase
|
|||
m_haveInvited = args.getBoolean( GameUtils.INVITED, false );
|
||||
m_overNotShown = true;
|
||||
|
||||
m_jniThreadRef = JNIThread.getRetained( m_rowid, true );
|
||||
|
||||
NFCUtils.register( m_activity, this ); // Don't seem to need to unregister...
|
||||
|
||||
setBackgroundColor();
|
||||
setKeepScreenOn();
|
||||
} // init
|
||||
|
||||
protected void onPause()
|
||||
{
|
||||
closeIfFinishing( false );
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart()
|
||||
{
|
||||
|
@ -640,21 +637,39 @@ public class BoardDelegate extends DelegateBase
|
|||
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
|
||||
protected void onDestroy()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void onDestroy()
|
||||
// {
|
||||
// GamesListDelegate.boardDestroyed( m_rowid );
|
||||
// super.onDestroy();
|
||||
// }
|
||||
|
||||
protected void onSaveInstanceState( Bundle outState )
|
||||
{
|
||||
outState.putInt( DLG_TITLE, m_dlgTitle );
|
||||
|
@ -2028,14 +2043,23 @@ public class BoardDelegate extends DelegateBase
|
|||
if ( null == m_jniGamePtr ) {
|
||||
try {
|
||||
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 );
|
||||
|
||||
if ( pairs.anyMissing( dictNames ) ) {
|
||||
showDictGoneFinish();
|
||||
} else {
|
||||
Assert.assertNull( m_gameLock );
|
||||
m_gameLock = new GameLock( m_rowid, true ).lock();
|
||||
if ( null == m_gameLock ) {
|
||||
m_gameLock = new GameLock( m_rowid, true ).lock();
|
||||
}
|
||||
|
||||
byte[] stream = GameUtils.savedGame( m_activity, m_gameLock );
|
||||
m_gi = new CurGameInfo( m_activity );
|
||||
|
@ -2110,13 +2134,13 @@ public class BoardDelegate extends DelegateBase
|
|||
}
|
||||
}
|
||||
};
|
||||
m_jniThread =
|
||||
new JNIThread( m_jniGamePtr, stream, m_gi,
|
||||
m_view, m_gameLock, m_activity, handler );
|
||||
m_jniThread = m_jniThreadRef
|
||||
.configure( m_jniGamePtr, stream, m_gi, m_view,
|
||||
m_gameLock, m_activity, handler );
|
||||
// see http://stackoverflow.com/questions/680180/where-to-stop-\
|
||||
// destroy-threads-in-android-service-class
|
||||
m_jniThread.setDaemon( true );
|
||||
m_jniThread.start();
|
||||
m_jniThread.setDaemonOnce( true ); // firing
|
||||
m_jniThread.startOnce();
|
||||
|
||||
m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi,
|
||||
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_xport ) {
|
||||
m_xport.waitToStop();
|
||||
m_xport = null;
|
||||
}
|
||||
|
||||
interruptBlockingThread();
|
||||
|
||||
if ( null != m_jniThread ) {
|
||||
m_jniThread.waitToStop( save );
|
||||
m_jniThread = null;
|
||||
}
|
||||
m_jniThread = null;
|
||||
m_view.stopHandling();
|
||||
|
||||
clearThis( this );
|
||||
|
@ -2417,12 +2433,25 @@ public class BoardDelegate extends DelegateBase
|
|||
GameUtils.takeSnapshot( m_activity, m_jniGamePtr, m_gi );
|
||||
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 = null;
|
||||
m_gi = null;
|
||||
|
||||
m_gameLock.unlock();
|
||||
// m_gameLock.unlock(); // likely the problem
|
||||
m_gameLock = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,15 +307,20 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
|
|||
// SyncedDraw interface implementation
|
||||
public void doJNIDraw()
|
||||
{
|
||||
boolean drew;
|
||||
boolean drew = false;
|
||||
synchronized( this ) {
|
||||
if ( !XwJNI.board_draw( m_jniGamePtr ) ) {
|
||||
DbgUtils.logf( "doJNIDraw: draw not complete" );
|
||||
if ( null != m_jniGamePtr ) {
|
||||
drew = XwJNI.board_draw( m_jniGamePtr );
|
||||
if ( !drew ) {
|
||||
DbgUtils.logf( "doJNIDraw: draw not complete" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 )
|
||||
|
|
|
@ -54,6 +54,7 @@ public class ChatDelegate extends DelegateBase {
|
|||
private EditText m_edit;
|
||||
private TableLayout m_layout;
|
||||
private ScrollView m_scroll;
|
||||
private JNIThread m_jniThreadRef;
|
||||
|
||||
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 )
|
||||
{
|
||||
TableRow row = (TableRow)inflate( R.layout.chat_row );
|
||||
|
@ -167,15 +183,15 @@ public class ChatDelegate extends DelegateBase {
|
|||
addRow( text, m_curPlayer );
|
||||
m_edit.setText( null );
|
||||
|
||||
JNIThread jniThread = JNIThread.getCurrent();
|
||||
if ( null != jniThread ) {
|
||||
jniThread.handle( JNIThread.JNICmd.CMD_SENDCHAT, text );
|
||||
} else {
|
||||
Intent result = new Intent();
|
||||
result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text );
|
||||
setResult( Activity.RESULT_OK, result );
|
||||
finish();
|
||||
}
|
||||
m_jniThreadRef.sendChat( text );
|
||||
// if ( null != jniThread ) {
|
||||
// jniThread.handle( JNIThread.JNICmd.CMD_SENDCHAT, text );
|
||||
// } else {
|
||||
// Intent result = new Intent();
|
||||
// result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text );
|
||||
// setResult( Activity.RESULT_OK, result );
|
||||
// finish();
|
||||
// }
|
||||
}
|
||||
// finish();
|
||||
break;
|
||||
|
|
|
@ -351,7 +351,7 @@ public class GameListItem extends LinearLayout
|
|||
@Override
|
||||
protected GameSummary doInBackground( Void... unused )
|
||||
{
|
||||
return DBUtils.getSummary( m_context, m_rowid, 150 );
|
||||
return DBUtils.getSummary( m_context, m_rowid, 500 );
|
||||
} // doInBackground
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,7 +28,9 @@ import junit.framework.Assert;
|
|||
// obtainable when other read locks are granted but not when a
|
||||
// write lock is. Write-locks are exclusive.
|
||||
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 boolean m_isForWrite;
|
||||
private int m_lockCount;
|
||||
|
@ -43,7 +45,7 @@ public class GameLock {
|
|||
m_rowid = rowid;
|
||||
m_isForWrite = isForWrite;
|
||||
m_lockCount = 0;
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>"
|
||||
+ "this: %H", rowid, isForWrite, this );
|
||||
DbgUtils.printStack();
|
||||
|
@ -69,26 +71,26 @@ public class GameLock {
|
|||
++m_lockCount;
|
||||
gotIt = true;
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
StackTraceElement[] trace
|
||||
= Thread.currentThread().getStackTrace();
|
||||
m_lockTrace = new StackTraceElement[trace.length];
|
||||
System.arraycopy( trace, 0, m_lockTrace, 0, trace.length );
|
||||
}
|
||||
} else if ( this == owner && ! m_isForWrite ) {
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "tryLock(): incrementing lock count" );
|
||||
}
|
||||
Assert.assertTrue( 0 == m_lockCount );
|
||||
++m_lockCount;
|
||||
gotIt = true;
|
||||
owner = null;
|
||||
} else if ( XWApp.DEBUG_LOCKS ) {
|
||||
} else if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "tryLock(): rowid %d already held by lock %H",
|
||||
m_rowid, owner );
|
||||
}
|
||||
}
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.tryLock %H (rowid=%d) => %b",
|
||||
this, m_rowid, gotIt );
|
||||
}
|
||||
|
@ -105,11 +107,10 @@ public class GameLock {
|
|||
public GameLock lock( long maxMillis )
|
||||
{
|
||||
GameLock result = null;
|
||||
final long assertTime = 2000;
|
||||
Assert.assertTrue( maxMillis < assertTime );
|
||||
Assert.assertTrue( maxMillis < ASSERT_TIME );
|
||||
long sleptTime = 0;
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid,
|
||||
maxMillis );
|
||||
}
|
||||
|
@ -120,9 +121,9 @@ public class GameLock {
|
|||
result = this;
|
||||
break;
|
||||
}
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
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.printStack( curOwner.m_lockTrace );
|
||||
DbgUtils.logf( "lock %H seeking stack:", this );
|
||||
|
@ -137,18 +138,18 @@ public class GameLock {
|
|||
break;
|
||||
}
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( 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 ) {
|
||||
} else if ( sleptTime >= ASSERT_TIME ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "lock %H overlocked", this );
|
||||
}
|
||||
Assert.fail();
|
||||
Assert.fail(); // firing
|
||||
}
|
||||
}
|
||||
// DbgUtils.logf( "GameLock.lock(%s) done", m_path );
|
||||
|
@ -167,7 +168,7 @@ public class GameLock {
|
|||
}
|
||||
--m_lockCount;
|
||||
|
||||
if ( XWApp.DEBUG_LOCKS ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) unlocked",
|
||||
this, m_rowid );
|
||||
}
|
||||
|
|
|
@ -369,6 +369,7 @@ public class GameUtils {
|
|||
ThumbCanvas canvas = new ThumbCanvas( context, thumb );
|
||||
XwJNI.board_setDraw( gamePtr, canvas );
|
||||
XwJNI.board_invalAll( gamePtr );
|
||||
Assert.assertNotNull( 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,
|
||||
int[] missingLang )
|
||||
{
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.eehouse.android.xw4.jni.GameSummary;
|
|||
import org.eehouse.android.xw4.jni.LastMoveInfo;
|
||||
import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
public class RelayService extends XWService
|
||||
|
@ -999,9 +1000,10 @@ public class RelayService extends XWService
|
|||
{
|
||||
DbgUtils.logdf( "RelayService::feedMessage: %d bytes for rowid %d",
|
||||
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" );
|
||||
// do nothing
|
||||
} else {
|
||||
RelayMsgSink sink = new RelayMsgSink();
|
||||
sink.setRowID( rowid );
|
||||
|
@ -1043,19 +1045,31 @@ public class RelayService extends XWService
|
|||
// if game has messages, open it and feed 'em to it.
|
||||
if ( null != forOne ) {
|
||||
BackMoveResult bmr = new BackMoveResult();
|
||||
sink.setRowID( rowIDs[ii] );
|
||||
long rowid = rowIDs[ii];
|
||||
sink.setRowID( rowid );
|
||||
// since BoardDelegate.feedMessages can't know:
|
||||
isLocalP[0] = false;
|
||||
if ( BoardDelegate.feedMessages( rowIDs[ii], forOne, s_addr )
|
||||
|| GameUtils.feedMessages( this, rowIDs[ii],
|
||||
forOne, s_addr,
|
||||
sink, bmr, isLocalP ) ) {
|
||||
boolean delivered = true;
|
||||
|
||||
JNIThread jniThread = JNIThread.getRetained( rowid, false );
|
||||
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] );
|
||||
bmrs.add( bmr );
|
||||
isLocals.add( isLocalP[0] );
|
||||
} else {
|
||||
DbgUtils.logf( "RelayService.process(): message for %s (rowid %d)"
|
||||
+ " not consumed", relayIDs[ii], rowIDs[ii] );
|
||||
+ " not consumed", relayIDs[ii], rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.LastMoveInfo;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
public class SMSService extends XWService {
|
||||
|
@ -709,8 +710,9 @@ public class SMSService extends XWService {
|
|||
} else {
|
||||
boolean[] isLocalP = new boolean[1];
|
||||
for ( long rowid : rowids ) {
|
||||
if ( BoardDelegate.feedMessage( rowid, msg, addr ) ) {
|
||||
// do nothing
|
||||
JNIThread jniThread = JNIThread.getRetained( rowid, false );
|
||||
if ( null != jniThread ) {
|
||||
jniThread.receive( msg, addr ).release();
|
||||
} else {
|
||||
SMSMsgSink sink = new SMSMsgSink( this );
|
||||
BackMoveResult bmr = new BackMoveResult();
|
||||
|
|
|
@ -38,8 +38,7 @@ public class XWApp extends Application {
|
|||
public static final boolean REMATCH_SUPPORTED = true;
|
||||
public static final boolean RELAYINVITE_SUPPORTED = false;
|
||||
public static final boolean ATTACH_SUPPORTED = false;
|
||||
public static final boolean DEBUG_LOCKS = false;
|
||||
public static final boolean LOG_LIFECYLE = false;
|
||||
public static final boolean LOG_LIFECYLE = true;
|
||||
public static final boolean DEBUG_EXP_TIMERS = false;
|
||||
public static final boolean GCM_IGNORED = false;
|
||||
public static final boolean UDP_ENABLED = true;
|
||||
|
|
|
@ -30,6 +30,8 @@ import android.os.Message;
|
|||
import java.lang.InterruptedException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -160,6 +162,8 @@ public class JNIThread extends Thread {
|
|||
private static final int kMinDivWidth = 10;
|
||||
private int m_connsIconID = 0;
|
||||
private String m_newDict = null;
|
||||
private long m_rowid;
|
||||
private int m_refCount;
|
||||
|
||||
private LinkedBlockingQueue<QueueElem> m_queue;
|
||||
|
||||
|
@ -173,27 +177,35 @@ public class JNIThread extends Thread {
|
|||
Object[] m_args;
|
||||
}
|
||||
|
||||
public JNIThread( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi,
|
||||
SyncedDraw drawer, GameLock lock, Context context,
|
||||
Handler handler )
|
||||
private JNIThread( long rowid )
|
||||
{
|
||||
synchronized( s_curThreads ) {
|
||||
s_curThreads.add( this );
|
||||
// DbgUtils.logf( "JNIThread(): added %H; now %d threads", this, s_curThreads.size() );
|
||||
m_rowid = rowid;
|
||||
m_queue = new LinkedBlockingQueue<QueueElem>();
|
||||
}
|
||||
|
||||
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;
|
||||
m_jniGamePtr = gamePtr.retain();
|
||||
m_gameAtStart = gameAtStart;
|
||||
m_gi = gi;
|
||||
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_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 ) {
|
||||
m_stopped = true;
|
||||
|
@ -210,12 +222,9 @@ public class JNIThread extends Thread {
|
|||
} catch ( java.lang.InterruptedException ie ) {
|
||||
DbgUtils.loge( ie );
|
||||
}
|
||||
|
||||
synchronized( s_curThreads ) {
|
||||
Assert.assertTrue( s_curThreads.contains( this ) );
|
||||
s_curThreads.remove( this );
|
||||
// DbgUtils.logf( "waitToStop: removed %H; now %d threads", this, s_curThreads.size() );
|
||||
}
|
||||
// m_jniGamePtr.release();
|
||||
// m_jniGamePtr = null;
|
||||
m_lock.unlock();
|
||||
}
|
||||
|
||||
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")
|
||||
public void run()
|
||||
{
|
||||
|
@ -637,7 +662,9 @@ public class JNIThread extends Thread {
|
|||
} else {
|
||||
DbgUtils.logf( "JNIThread.run(): exiting without saving" );
|
||||
}
|
||||
XwJNI.threadDone();
|
||||
m_jniGamePtr.release();
|
||||
m_jniGamePtr = null;
|
||||
// XwJNI.threadDone();
|
||||
} // run
|
||||
|
||||
public void handleBkgrnd( JNICmd cmd, Object... args )
|
||||
|
@ -647,6 +674,17 @@ public class JNIThread extends Thread {
|
|||
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 )
|
||||
{
|
||||
QueueElem elem = new QueueElem( cmd, true, args );
|
||||
|
@ -667,10 +705,54 @@ public class JNIThread extends Thread {
|
|||
return XwJNI.server_do( gamePtr );
|
||||
}
|
||||
|
||||
// public void run( boolean isUI, Runnable runnable )
|
||||
// {
|
||||
// Object[] args = { runnable };
|
||||
// QueueElem elem = new QueueElem( JNICmd.CMD_RUN, isUI, args );
|
||||
// m_queue.add( elem );
|
||||
// }
|
||||
private static Map<Long, JNIThread> s_instances = new HashMap<Long, JNIThread>();
|
||||
private void retain_sync()
|
||||
{
|
||||
++m_refCount;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,18 +34,30 @@ public class XwJNI {
|
|||
|
||||
public static class GamePtr {
|
||||
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 synchronized GamePtr retain()
|
||||
{
|
||||
++m_refCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
// 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
|
||||
public void release()
|
||||
public synchronized void release()
|
||||
{
|
||||
if ( 0 != m_ptr ) {
|
||||
game_dispose( this );
|
||||
m_ptr = 0;
|
||||
if ( 0 == --m_refCount ) {
|
||||
if ( 0 != m_ptr ) {
|
||||
game_dispose( this );
|
||||
m_ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue