mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-08 05:24:39 +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.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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue