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.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,

View file

@ -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;
}
}

View file

@ -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 )

View file

@ -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;

View file

@ -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

View file

@ -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 );
}

View file

@ -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 )
{

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.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 );
}
}
}

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.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();

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}
}