diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index 1534dc3d7..1bd4d371d 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -109,8 +109,10 @@ map_thread( EnvThreadInfo* ti, JNIEnv* env ) XP_ASSERT( !!firstEmpty ); firstEmpty->owner = self; firstEmpty->env = env; - /* XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__, */ - /* firstEmpty - ti->entries, env, (int)self ); */ +#ifdef LOG_MAPPING + XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__, + firstEmpty - ti->entries, env, (int)self ); +#endif } pthread_mutex_unlock( &ti->mtxThreads ); @@ -134,6 +136,11 @@ map_remove( EnvThreadInfo* ti, JNIEnv* env ) found = env == ti->entries[ii].env; if ( found ) { XP_ASSERT( pthread_self() == ti->entries[ii].owner ); +#ifdef LOG_MAPPING + XP_LOGF( "%s: UNMAPPED env %p to thread %x", __func__, + ti->entries[ii].env, + (int)ti->entries[ii].owner ); +#endif ti->entries[ii].env = NULL; ti->entries[ii].owner = 0; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java index 1512f3223..cfa8e0410 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java @@ -61,6 +61,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.JNIThread.*; +import org.eehouse.android.xw4.jni.XwJNI.GamePtr; public class BoardDelegate extends DelegateBase implements TransportProcs.TPMsgHandler, View.OnClickListener, @@ -83,7 +84,7 @@ public class BoardDelegate extends DelegateBase private Activity m_activity; private BoardView m_view; - private int m_jniGamePtr; + private GamePtr m_jniGamePtr; private GameLock m_gameLock; private CurGameInfo m_gi; private GameSummary m_summary; @@ -865,8 +866,8 @@ public class BoardDelegate extends DelegateBase int id = item.getItemId(); switch ( id ) { case R.id.board_menu_done: - int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr, - m_view.getCurPlayer() ); + int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr.ptr(), + m_view.getCurPlayer() ); if ( XWApp.MAX_TRAY_TILES > nTiles ) { showNotAgainDlgThen( R.string.not_again_done, R.string.key_notagain_done, @@ -1415,9 +1416,9 @@ public class BoardDelegate extends DelegateBase private void dropRelayAndRestart() { CommsAddrRec addr = new CommsAddrRec(); - XwJNI.comms_getAddr( m_jniGamePtr, addr ); + XwJNI.comms_getAddr( m_jniGamePtr.ptr(), addr ); addr.remove( CommsConnType.COMMS_CONN_RELAY ); - XwJNI.comms_setAddr( m_jniGamePtr, addr ); + XwJNI.comms_setAddr( m_jniGamePtr.ptr(), addr ); finish(); @@ -1642,7 +1643,7 @@ public class BoardDelegate extends DelegateBase public void playerScoreHeld( int player ) { LastMoveInfo lmi = new LastMoveInfo(); - XwJNI.model_getPlayersLastScore( m_jniGamePtr, player, lmi ); + XwJNI.model_getPlayersLastScore( m_jniGamePtr.ptr(), player, lmi ); String expl = lmi.format( m_activity ); if ( null == expl || 0 == expl.length() ) { expl = getString( R.string.no_moves_made ); @@ -2017,7 +2018,7 @@ public class BoardDelegate extends DelegateBase private void loadGame( boolean isStart ) { - if ( 0 == m_jniGamePtr ) { + if ( null == m_jniGamePtr ) { try { String gameName = DBUtils.getName( m_activity, m_rowid ); String[] dictNames = GameUtils.dictNames( m_activity, m_rowid ); @@ -2043,19 +2044,19 @@ public class BoardDelegate extends DelegateBase m_jniGamePtr = XwJNI.initJNI( m_rowid ); if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { - m_xport = new CommsTransport( m_jniGamePtr, m_activity, this, - m_rowid, m_gi.serverRole ); + m_xport = new CommsTransport( m_activity, this, m_rowid, + m_gi.serverRole ); } CommonPrefs cp = CommonPrefs.get( m_activity ); if ( null == stream || - ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, + ! XwJNI.game_makeFromStream( m_jniGamePtr.ptr(), stream, m_gi, dictNames, pairs.m_bytes, pairs.m_paths, langName, m_utils, m_jniu, null, cp, m_xport ) ) { - XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, + XwJNI.game_makeNewGame( m_jniGamePtr.ptr(), m_gi, m_utils, m_jniu, null, cp, m_xport, dictNames, pairs.m_bytes, pairs.m_paths, langName ); @@ -2212,7 +2213,7 @@ public class BoardDelegate extends DelegateBase { if ( null != m_connTypes && m_connTypes.contains( CommsConnType.COMMS_CONN_BT ) ) { - CommsAddrRec[] addrs = XwJNI.comms_getAddrs( m_jniGamePtr ); + CommsAddrRec[] addrs = XwJNI.comms_getAddrs( m_jniGamePtr.ptr() ); for ( CommsAddrRec addr : addrs ) { if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) { BTService.pingHost( m_activity, addr.bt_btAddr, @@ -2370,7 +2371,7 @@ public class BoardDelegate extends DelegateBase private void waitCloseGame( boolean save ) { - if ( 0 != m_jniGamePtr ) { + if ( null != m_jniGamePtr ) { if ( null != m_xport ) { m_xport.waitToStop(); m_xport = null; @@ -2394,8 +2395,8 @@ public class BoardDelegate extends DelegateBase DBUtils.saveThumbnail( m_activity, m_gameLock, thumb ); } - XwJNI.game_dispose( m_jniGamePtr ); - m_jniGamePtr = 0; + m_jniGamePtr.release(); + m_jniGamePtr = null; m_gi = null; m_gameLock.unlock(); @@ -2638,7 +2639,7 @@ public class BoardDelegate extends DelegateBase private static boolean doRematchIf( Activity activity, DelegateBase dlgt, long rowid, GameSummary summary, - CurGameInfo gi, int jniGamePtr ) + CurGameInfo gi, GamePtr jniGamePtr ) { boolean success = false; if ( XWApp.REMATCH_SUPPORTED ) { @@ -2656,7 +2657,7 @@ public class BoardDelegate extends DelegateBase } doIt = false; } else { - CommsAddrRec[] addrs = XwJNI.comms_getAddrs( jniGamePtr ); + CommsAddrRec[] addrs = XwJNI.comms_getAddrs( jniGamePtr.ptr() ); for ( int ii = 0; ii < addrs.length; ++ii ) { CommsAddrRec addr = addrs[ii]; if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) { @@ -2669,7 +2670,7 @@ public class BoardDelegate extends DelegateBase } if ( addr.contains( CommsConnType.COMMS_CONN_RELAY ) ) { Assert.assertNull( relayID ); - relayID = XwJNI.comms_formatRelayID( jniGamePtr, ii ); + relayID = XwJNI.comms_formatRelayID( jniGamePtr.ptr(), ii ); } } } @@ -2696,11 +2697,11 @@ public class BoardDelegate extends DelegateBase if ( lock.tryLock() ) { GameSummary summary = DBUtils.getSummary( activity, lock ); CurGameInfo gi = new CurGameInfo( activity ); - int gamePtr = GameUtils.loadMakeGame( activity, gi, lock ); + GamePtr gamePtr = GameUtils.loadMakeGame( activity, gi, lock ); doRematchIf( activity, null, rowID, summary, gi, gamePtr ); - XwJNI.game_dispose( gamePtr ); + gamePtr.release(); lock.unlock(); } else { DbgUtils.logf( "setupRematchFor(): unable to lock game" ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index d6b9b61e4..9489aa3af 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -58,7 +58,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { private int m_defaultFontHt; private int m_mediumFontHt; private Runnable m_invalidator; - private int m_jniGamePtr; + private XwJNI.GamePtr m_jniGamePtr; private CurGameInfo m_gi; private boolean m_isSolo; private int m_layoutWidth; @@ -270,7 +270,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { // BoardHandler interface implementation public void startHandling( Activity parent, JNIThread thread, - int gamePtr, CurGameInfo gi, + XwJNI.GamePtr gamePtr, CurGameInfo gi, CommsConnTypeSet connTypes ) { m_parent = parent; @@ -298,7 +298,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { public void stopHandling() { m_jniThread = null; - m_jniGamePtr = 0; + m_jniGamePtr = null; if ( null != m_canvas ) { m_canvas.setJNIThread( null ); } @@ -309,7 +309,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { { boolean drew; synchronized( this ) { - if ( !XwJNI.board_draw( m_jniGamePtr ) ) { + if ( !XwJNI.board_draw( m_jniGamePtr.ptr() ) ) { DbgUtils.logf( "doJNIDraw: draw not complete" ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java index a471b4000..6045d0570 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java @@ -44,7 +44,6 @@ public class CommsTransport implements TransportProcs, NetStateCache.StateChangedIf { private Selector m_selector; private SocketChannel m_socketChannel; - private int m_jniGamePtr; private CommsAddrRec m_relayAddr; private String m_useHost; private JNIThread m_jniThread; @@ -64,11 +63,9 @@ public class CommsTransport implements TransportProcs, private byte[] m_packetIn; private int m_haveLen = -1; - public CommsTransport( int jniGamePtr, Context context, - TransportProcs.TPMsgHandler handler, + public CommsTransport( Context context, TransportProcs.TPMsgHandler handler, long rowid, DeviceRole role ) { - m_jniGamePtr = jniGamePtr; m_context = context; m_tpHandler = handler; m_rowid = rowid; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfigDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfigDelegate.java index f9fe90c36..e7468a8ca 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfigDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfigDelegate.java @@ -543,12 +543,12 @@ public class GameConfigDelegate extends DelegateBase m_giOrig = new CurGameInfo( m_activity ); GameLock gameLock = new GameLock( m_rowid, false ).lock(); - int gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, gameLock ); - if ( 0 == gamePtr ) { + XwJNI.GamePtr gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, gameLock ); + if ( null == gamePtr ) { showDictGoneFinish(); } else { - m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0 - || XwJNI.comms_isConnected( gamePtr ); + m_gameStarted = XwJNI.model_getNMoves( gamePtr.ptr() ) > 0 + || XwJNI.comms_isConnected( gamePtr.ptr() ); if ( m_gameStarted ) { if ( null == m_gameLockedCheck ) { @@ -565,9 +565,9 @@ public class GameConfigDelegate extends DelegateBase } m_carOrig = new CommsAddrRec(); - if ( XwJNI.game_hasComms( gamePtr ) ) { - XwJNI.comms_getAddr( gamePtr, m_carOrig ); - m_remoteAddrs = XwJNI.comms_getAddrs( gamePtr ); + if ( XwJNI.game_hasComms( gamePtr.ptr() ) ) { + XwJNI.comms_getAddr( gamePtr.ptr(), m_carOrig ); + m_remoteAddrs = XwJNI.comms_getAddrs( gamePtr.ptr() ); } else if ( !localOnlyGame() ) { String relayName = XWPrefs.getDefaultRelayHost( m_activity ); int relayPort = XWPrefs.getDefaultRelayPort( m_activity ); @@ -575,7 +575,7 @@ public class GameConfigDelegate extends DelegateBase relayPort ); } m_conTypes = (CommsConnTypeSet)m_carOrig.conTypes.clone(); - XwJNI.game_dispose( gamePtr ); + gamePtr.release(); gameLock.unlock(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 1fe10ae8b..6ac73d38e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -44,11 +44,12 @@ import org.json.JSONObject; import junit.framework.Assert; import org.eehouse.android.xw4.jni.*; -import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.LastMoveInfo; +import org.eehouse.android.xw4.jni.XwJNI.GamePtr; +import org.eehouse.android.xw4.loc.LocUtils; public class GameUtils { @@ -107,23 +108,23 @@ public class GameUtils { // loadMakeGame, if making a new game, will add comms as long // as DeviceRole.SERVER_STANDALONE != gi.serverRole - int gamePtr = loadMakeGame( context, gi, lockSrc ); + GamePtr gamePtr = loadMakeGame( context, gi, lockSrc ); String[] dictNames = gi.dictNames(); DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); - if ( XwJNI.game_hasComms( gamePtr ) ) { + if ( XwJNI.game_hasComms( gamePtr.ptr() ) ) { addr = new CommsAddrRec(); - XwJNI.comms_getAddr( gamePtr, addr ); + XwJNI.comms_getAddr( gamePtr.ptr(), addr ); if ( 0 == addr.conTypes.size() ) { String relayName = XWPrefs.getDefaultRelayHost( context ); int relayPort = XWPrefs.getDefaultRelayPort( context ); XwJNI.comms_getInitialAddr( addr, relayName, relayPort ); } } - XwJNI.game_dispose( gamePtr ); - + gamePtr.release(); + gamePtr = XwJNI.initJNI(); - XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get( context ), + XwJNI.game_makeNewGame( gamePtr.ptr(), gi, JNIUtilsImpl.get( context ), CommonPrefs.get( context ), dictNames, pairs.m_bytes, pairs.m_paths, gi.langName() ); @@ -132,7 +133,7 @@ public class GameUtils { } if ( null != addr ) { - XwJNI.comms_setAddr( gamePtr, addr ); + XwJNI.comms_setAddr( gamePtr.ptr(), addr ); } if ( null == lockDest ) { @@ -178,15 +179,16 @@ public class GameUtils { } private static GameSummary summarizeAndClose( Context context, - GameLock lock, int gamePtr, + GameLock lock, + GamePtr gamePtr, CurGameInfo gi ) { GameSummary summary = new GameSummary( context, gi ); - XwJNI.game_summarize( gamePtr, summary ); + XwJNI.game_summarize( gamePtr.ptr(), summary ); DBUtils.saveSummary( context, lock, summary ); - XwJNI.game_dispose( gamePtr ); + gamePtr.release(); return summary; } @@ -194,8 +196,8 @@ public class GameUtils { { GameSummary result = null; CurGameInfo gi = new CurGameInfo( context ); - int gamePtr = loadMakeGame( context, gi, lock ); - if ( 0 < gamePtr ) { + GamePtr gamePtr = loadMakeGame( context, gi, lock ); + if ( null != gamePtr ) { result = summarizeAndClose( context, lock, gamePtr, gi ); } return result; @@ -266,23 +268,23 @@ public class GameUtils { return LocUtils.getString( context, R.string.game_fmt, count ); } - public static int loadMakeGame( Context context, CurGameInfo gi, - TransportProcs tp, GameLock lock ) + public static GamePtr loadMakeGame( Context context, CurGameInfo gi, + TransportProcs tp, GameLock lock ) { return loadMakeGame( context, gi, null, tp, lock ); } - public static int loadMakeGame( Context context, CurGameInfo gi, + public static GamePtr loadMakeGame( Context context, CurGameInfo gi, GameLock lock ) { return loadMakeGame( context, gi, null, null, lock ); } - public static int loadMakeGame( Context context, CurGameInfo gi, - UtilCtxt util, TransportProcs tp, - GameLock lock ) + public static GamePtr loadMakeGame( Context context, CurGameInfo gi, + UtilCtxt util, TransportProcs tp, + GameLock lock ) { - int gamePtr = 0; + GamePtr gamePtr = null; byte[] stream = savedGame( context, lock ); if ( null == stream ) { @@ -299,14 +301,14 @@ public class GameUtils { String langName = gi.langName(); boolean madeGame = - XwJNI.game_makeFromStream( gamePtr, stream, gi, + XwJNI.game_makeFromStream( gamePtr.ptr(), stream, gi, dictNames, pairs.m_bytes, pairs.m_paths, langName, util, JNIUtilsImpl.get( context ), CommonPrefs.get(context), tp); if ( !madeGame ) { - XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(context), + XwJNI.game_makeNewGame( gamePtr.ptr(), gi, JNIUtilsImpl.get(context), CommonPrefs.get(context), dictNames, pairs.m_bytes, pairs.m_paths, langName ); @@ -322,19 +324,18 @@ public class GameUtils { GameLock lock = new GameLock( rowid, false ); if ( lock.tryLock() ) { CurGameInfo gi = new CurGameInfo( activity ); - int gamePtr = loadMakeGame( activity, gi, lock ); - if ( 0 != gamePtr ) { + GamePtr gamePtr = loadMakeGame( activity, gi, lock ); + if ( null != gamePtr ) { thumb = takeSnapshot( activity, gamePtr, gi ); - XwJNI.game_dispose( gamePtr ); + gamePtr.release(); DBUtils.saveThumbnail( activity, lock, thumb ); } - lock.unlock(); } return thumb; } - public static Bitmap takeSnapshot( Context context, int gamePtr, + public static Bitmap takeSnapshot( Context context, GamePtr gamePtr, CurGameInfo gi ) { Bitmap thumb = null; @@ -361,13 +362,13 @@ public class GameUtils { thumb = Bitmap.createBitmap( size, size, Bitmap.Config.ARGB_8888 ); - XwJNI.board_figureLayout( gamePtr, gi, 0, 0, size, size, + XwJNI.board_figureLayout( gamePtr.ptr(), gi, 0, 0, size, size, 0, 0, 0, 20, 20, false, null ); ThumbCanvas canvas = new ThumbCanvas( context, thumb ); - XwJNI.board_setDraw( gamePtr, canvas ); - XwJNI.board_invalAll( gamePtr ); - XwJNI.board_draw( gamePtr ); + XwJNI.board_setDraw( gamePtr.ptr(), canvas ); + XwJNI.board_invalAll( gamePtr.ptr() ); + XwJNI.board_draw( gamePtr.ptr() ); } } } @@ -400,18 +401,18 @@ public class GameUtils { } } - public static long saveGame( Context context, int gamePtr, + public static long saveGame( Context context, GamePtr gamePtr, CurGameInfo gi, GameLock lock, boolean setCreate ) { - byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); + byte[] stream = XwJNI.game_saveToStream( gamePtr.ptr(), gi ); return saveGame( context, stream, lock, setCreate ); } - public static long saveNewGame( Context context, int gamePtr, + public static long saveNewGame( Context context, GamePtr gamePtr, CurGameInfo gi, long groupID ) { - byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); + byte[] stream = XwJNI.game_saveToStream( gamePtr.ptr(), gi ); GameLock lock = DBUtils.saveNewGame( context, stream, groupID, null ); long rowid = lock.getRowid(); lock.unlock(); @@ -890,19 +891,19 @@ public class GameUtils { if ( null != lock ) { CurGameInfo gi = new CurGameInfo( context ); FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); - int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); - if ( 0 != gamePtr ) { - XwJNI.comms_resendAll( gamePtr, false, false ); + GamePtr gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); + if ( null != gamePtr ) { + XwJNI.comms_resendAll( gamePtr.ptr(), false, false ); for ( byte[] msg : msgs ) { Assert.assertNotNull( ret ); - draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) + draw = XwJNI.game_receiveMessage( gamePtr.ptr(), msg, ret ) || draw; } - XwJNI.comms_ackAny( gamePtr ); + XwJNI.comms_ackAny( gamePtr.ptr() ); // update gi to reflect changes due to messages - XwJNI.game_getGi( gamePtr, gi ); + XwJNI.game_getGi( gamePtr.ptr(), gi ); if ( draw && XWPrefs.getThumbEnabled( context ) ) { Bitmap bitmap = takeSnapshot( context, gamePtr, gi ); @@ -915,7 +916,7 @@ public class GameUtils { bmr.m_chatFrom = feedImpl.m_chatFrom; } else { LastMoveInfo lmi = new LastMoveInfo(); - XwJNI.model_getPlayersLastScore( gamePtr, -1, lmi ); + XwJNI.model_getPlayersLastScore( gamePtr.ptr(), -1, lmi ); bmr.m_lmi = lmi; } } @@ -971,8 +972,8 @@ public class GameUtils { DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); - int gamePtr = XwJNI.initJNI( rowid ); - XwJNI.game_makeFromStream( gamePtr, stream, gi, dictNames, + GamePtr gamePtr = XwJNI.initJNI( rowid ); + XwJNI.game_makeFromStream( gamePtr.ptr(), stream, gi, dictNames, pairs.m_bytes, pairs.m_paths, gi.langName(), JNIUtilsImpl.get(context), @@ -1011,7 +1012,7 @@ public class GameUtils { String[] dictNames = gi.dictNames(); DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); String langName = gi.langName(); - int gamePtr = XwJNI.initJNI( lock.getRowid() ); + GamePtr gamePtr = XwJNI.initJNI( lock.getRowid() ); boolean madeGame = false; CommonPrefs cp = CommonPrefs.get( context ); @@ -1020,7 +1021,7 @@ public class GameUtils { } else { byte[] stream = savedGame( context, lock ); // Will fail if there's nothing in the stream but a gi. - madeGame = XwJNI.game_makeFromStream( gamePtr, stream, + madeGame = XwJNI.game_makeFromStream( gamePtr.ptr(), stream, new CurGameInfo(context), dictNames, pairs.m_bytes, pairs.m_paths, langName, @@ -1029,7 +1030,7 @@ public class GameUtils { } if ( forceNew || !madeGame ) { - XwJNI.game_makeNewGame( gamePtr, gi, util, + XwJNI.game_makeNewGame( gamePtr.ptr(), gi, util, JNIUtilsImpl.get(context), (DrawCtx)null, cp, sink, dictNames, pairs.m_bytes, @@ -1037,20 +1038,19 @@ public class GameUtils { } if ( null != car ) { - XwJNI.comms_setAddr( gamePtr, car ); + XwJNI.comms_setAddr( gamePtr.ptr(), car ); } if ( null != sink ) { - JNIThread.tryConnectClient( gamePtr, gi ); + JNIThread.tryConnectClient( gamePtr.ptr(), gi ); } saveGame( context, gamePtr, gi, lock, false ); GameSummary summary = new GameSummary( context, gi ); - XwJNI.game_summarize( gamePtr, summary ); + XwJNI.game_summarize( gamePtr.ptr(), summary ); + gamePtr.release(); DBUtils.saveSummary( context, lock, summary ); - - XwJNI.game_dispose( gamePtr ); } // applyChanges public static void doConfig( Activity activity, long rowid, Class clazz ) @@ -1216,9 +1216,10 @@ public class GameUtils { if ( lock.tryLock() ) { CurGameInfo gi = new CurGameInfo( m_context ); m_sink = new MultiMsgSink( m_context, rowid ); - int gamePtr = loadMakeGame( m_context, gi, m_sink, lock ); - if ( 0 != gamePtr ) { - XwJNI.comms_resendAll( gamePtr, true, false ); + GamePtr gamePtr = loadMakeGame( m_context, gi, m_sink, lock ); + if ( null != gamePtr ) { + XwJNI.comms_resendAll( gamePtr.ptr(), true, false ); + gamePtr.release(); } lock.unlock(); } else { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java index 0d2517108..e0cb614df 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java @@ -26,7 +26,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet; public interface BoardHandler { void startHandling( Activity parent, JNIThread thread, - int gamePtr, CurGameInfo gi, + XwJNI.GamePtr gamePtr, CurGameInfo gi, CommsConnTypeSet connTypes ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 79d0de388..6fc33eb3a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -43,6 +43,7 @@ import org.eehouse.android.xw4.XWPrefs; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.DrawCtx; +import org.eehouse.android.xw4.jni.XwJNI.GamePtr; import junit.framework.Assert; @@ -132,7 +133,7 @@ public class JNIThread extends Thread { private boolean m_stopped = false; private boolean m_saveOnStop = false; - private int m_jniGamePtr; + private GamePtr m_jniGamePtr; private byte[] m_gameAtStart; private GameLock m_lock; private Context m_context; @@ -155,7 +156,7 @@ public class JNIThread extends Thread { Object[] m_args; } - public JNIThread( int gamePtr, byte[] gameAtStart, CurGameInfo gi, + public JNIThread( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi, SyncedDraw drawer, GameLock lock, Context context, Handler handler ) { @@ -219,11 +220,11 @@ public class JNIThread extends Thread { private boolean toggleTray() { boolean draw; - int state = XwJNI.board_getTrayVisState( m_jniGamePtr ); + int state = XwJNI.board_getTrayVisState( m_jniGamePtr.ptr() ); if ( state == XwJNI.TRAY_REVEALED ) { - draw = XwJNI.board_hideTray( m_jniGamePtr ); + draw = XwJNI.board_hideTray( m_jniGamePtr.ptr() ); } else { - draw = XwJNI.board_showTray( m_jniGamePtr ); + draw = XwJNI.board_showTray( m_jniGamePtr.ptr() ); } return draw; } @@ -239,7 +240,7 @@ public class JNIThread extends Thread { BoardDims dims = new BoardDims(); boolean squareTiles = XWPrefs.getSquareTiles( m_context ); - XwJNI.board_figureLayout( m_jniGamePtr, m_gi, 0, 0, width, height, + XwJNI.board_figureLayout( m_jniGamePtr.ptr(), m_gi, 0, 0, width, height, 150 /*scorePct*/, 200 /*trayPct*/, width, fontWidth, fontHeight, squareTiles, dims /* out param */ ); @@ -255,7 +256,7 @@ public class JNIThread extends Thread { ConnStatusHandler.clearRect(); } - XwJNI.board_applyLayout( m_jniGamePtr, dims ); + XwJNI.board_applyLayout( m_jniGamePtr.ptr(), dims ); m_drawer.dimsChanged( dims ); } @@ -276,7 +277,7 @@ public class JNIThread extends Thread { private void checkButtons() { synchronized( m_gsi ) { - XwJNI.game_getState( m_jniGamePtr, m_gsi ); + XwJNI.game_getState( m_jniGamePtr.ptr(), m_gsi ); } Message.obtain( m_handler, TOOLBAR_STATES ).sendToTarget(); } @@ -286,27 +287,27 @@ public class JNIThread extends Thread { // If server has any work to do, e.g. clean up after showing a // remote- or robot-moved dialog, let it do so before saving // state. In some cases it'll otherwise drop the move. - XwJNI.server_do( m_jniGamePtr ); + XwJNI.server_do( m_jniGamePtr.ptr() ); // And let it tell the relay (if any) it's leaving // XwJNI.comms_stop( m_jniGamePtr ); - XwJNI.game_getGi( m_jniGamePtr, m_gi ); + XwJNI.game_getGi( m_jniGamePtr.ptr(), m_gi ); if ( null != m_newDict ) { m_gi.dictName = m_newDict; } - byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi ); + byte[] state = XwJNI.game_saveToStream( m_jniGamePtr.ptr(), m_gi ); if ( Arrays.equals( m_gameAtStart, state ) ) { // DbgUtils.logf( "no change in game; can skip saving" ); } else { synchronized( this ) { Assert.assertNotNull( m_lock ); GameSummary summary = new GameSummary( m_context, m_gi ); - XwJNI.game_summarize( m_jniGamePtr, summary ); + XwJNI.game_summarize( m_jniGamePtr.ptr(), summary ); DBUtils.saveGame( m_context, m_lock, state, false ); DBUtils.saveSummary( m_context, m_lock, summary ); // There'd better be no way for saveGame above to fail! - XwJNI.game_saveSucceeded( m_jniGamePtr ); + XwJNI.game_saveSucceeded( m_jniGamePtr.ptr() ); } } } @@ -354,12 +355,12 @@ public class JNIThread extends Thread { break; case CMD_SETDRAW: - XwJNI.board_setDraw( m_jniGamePtr, (DrawCtx)args[0] ); - XwJNI.board_invalAll( m_jniGamePtr ); + XwJNI.board_setDraw( m_jniGamePtr.ptr(), (DrawCtx)args[0] ); + XwJNI.board_invalAll( m_jniGamePtr.ptr() ); break; case CMD_INVALALL: - XwJNI.board_invalAll( m_jniGamePtr ); + XwJNI.board_invalAll( m_jniGamePtr.ptr() ); draw = true; break; @@ -368,7 +369,7 @@ public class JNIThread extends Thread { BoardDims dims = null; if ( args0 instanceof BoardDims ) { dims = (BoardDims)args0; - XwJNI.board_applyLayout( m_jniGamePtr, dims ); + XwJNI.board_applyLayout( m_jniGamePtr.ptr(), dims ); } else { doLayout( (Integer)args0, (Integer)args[1], (Integer)args[2], (Integer)args[3] ); @@ -379,29 +380,29 @@ public class JNIThread extends Thread { break; case CMD_RESET: - XwJNI.comms_resetSame( m_jniGamePtr ); + XwJNI.comms_resetSame( m_jniGamePtr.ptr() ); // FALLTHRU case CMD_START: - draw = tryConnectClient( m_jniGamePtr, m_gi ); + draw = tryConnectClient( m_jniGamePtr.ptr(), m_gi ); break; case CMD_SWITCHCLIENT: - XwJNI.server_reset( m_jniGamePtr ); - XwJNI.server_initClientConnection( m_jniGamePtr ); - draw = XwJNI.server_do( m_jniGamePtr ); + XwJNI.server_reset( m_jniGamePtr.ptr() ); + XwJNI.server_initClientConnection( m_jniGamePtr.ptr() ); + draw = XwJNI.server_do( m_jniGamePtr.ptr() ); break; case CMD_DO: if ( nextSame( JNICmd.CMD_DO ) ) { continue; } - draw = XwJNI.server_do( m_jniGamePtr ); + draw = XwJNI.server_do( m_jniGamePtr.ptr() ); break; case CMD_RECEIVE: CommsAddrRec ret = (CommsAddrRec)args[1]; Assert.assertNotNull( ret ); - draw = XwJNI.game_receiveMessage( m_jniGamePtr, + draw = XwJNI.game_receiveMessage( m_jniGamePtr.ptr(), (byte[])args[0], ret ); handle( JNICmd.CMD_DO ); if ( draw ) { @@ -411,21 +412,21 @@ public class JNIThread extends Thread { case CMD_TRANSFAIL: CommsConnType typ = (CommsConnType)args[0]; - XwJNI.comms_transportFailed( m_jniGamePtr, typ ); + XwJNI.comms_transportFailed( m_jniGamePtr.ptr(), typ ); break; case CMD_PREFS_CHANGE: // need to inval all because some of prefs, // e.g. colors, aren't known by common code so // board_prefsChanged's return value isn't enough. - XwJNI.board_invalAll( m_jniGamePtr ); - XwJNI.board_server_prefsChanged( m_jniGamePtr, + XwJNI.board_invalAll( m_jniGamePtr.ptr() ); + XwJNI.board_server_prefsChanged( m_jniGamePtr.ptr(), CommonPrefs.get( m_context ) ); draw = true; break; case CMD_PEN_DOWN: - draw = XwJNI.board_handlePenDown( m_jniGamePtr, + draw = XwJNI.board_handlePenDown( m_jniGamePtr.ptr(), ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), barr ); @@ -434,12 +435,12 @@ public class JNIThread extends Thread { if ( nextSame( JNICmd.CMD_PEN_MOVE ) ) { continue; } - draw = XwJNI.board_handlePenMove( m_jniGamePtr, + draw = XwJNI.board_handlePenMove( m_jniGamePtr.ptr(), ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue() ); break; case CMD_PEN_UP: - draw = XwJNI.board_handlePenUp( m_jniGamePtr, + draw = XwJNI.board_handlePenUp( m_jniGamePtr.ptr(), ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue() ); break; @@ -450,30 +451,30 @@ public class JNIThread extends Thread { break; case CMD_COMMIT: - draw = XwJNI.board_commitTurn( m_jniGamePtr ); + draw = XwJNI.board_commitTurn( m_jniGamePtr.ptr() ); break; case CMD_JUGGLE: - draw = XwJNI.board_juggleTray( m_jniGamePtr ); + draw = XwJNI.board_juggleTray( m_jniGamePtr.ptr() ); break; case CMD_FLIP: - draw = XwJNI.board_flip( m_jniGamePtr ); + draw = XwJNI.board_flip( m_jniGamePtr.ptr() ); break; case CMD_TOGGLE_TRAY: draw = toggleTray(); break; case CMD_TRADE: - draw = XwJNI.board_beginTrade( m_jniGamePtr ); + draw = XwJNI.board_beginTrade( m_jniGamePtr.ptr() ); break; case CMD_CANCELTRADE: - draw = XwJNI.board_endTrade( m_jniGamePtr ); + draw = XwJNI.board_endTrade( m_jniGamePtr.ptr() ); break; case CMD_UNDO_CUR: - draw = XwJNI.board_replaceTiles( m_jniGamePtr ) - || XwJNI.board_redoReplacedTiles( m_jniGamePtr ); + draw = XwJNI.board_replaceTiles( m_jniGamePtr.ptr() ) + || XwJNI.board_redoReplacedTiles( m_jniGamePtr.ptr() ); break; case CMD_UNDO_LAST: - XwJNI.server_handleUndo( m_jniGamePtr ); + XwJNI.server_handleUndo( m_jniGamePtr.ptr() ); draw = true; break; @@ -482,7 +483,7 @@ public class JNIThread extends Thread { if ( nextSame( elem.m_cmd ) ) { continue; } - draw = XwJNI.board_requestHint( m_jniGamePtr, false, + draw = XwJNI.board_requestHint( m_jniGamePtr.ptr(), false, JNICmd.CMD_PREV_HINT==elem.m_cmd, barr ); if ( barr[0] ) { @@ -492,39 +493,39 @@ public class JNIThread extends Thread { break; case CMD_TOGGLEZOOM: - XwJNI.board_zoom( m_jniGamePtr, 0 , barr ); + XwJNI.board_zoom( m_jniGamePtr.ptr(), 0 , barr ); int zoomBy = 0; if ( barr[1] ) { // always go out if possible zoomBy = -5; } else if ( barr[0] ) { zoomBy = 5; } - draw = XwJNI.board_zoom( m_jniGamePtr, zoomBy, barr ); + draw = XwJNI.board_zoom( m_jniGamePtr.ptr(), zoomBy, barr ); break; case CMD_ZOOM: - draw = XwJNI.board_zoom( m_jniGamePtr, + draw = XwJNI.board_zoom( m_jniGamePtr.ptr(), ((Integer)args[0]).intValue(), barr ); break; case CMD_VALUES: - draw = XwJNI.board_toggle_showValues( m_jniGamePtr ); + draw = XwJNI.board_toggle_showValues( m_jniGamePtr.ptr() ); break; case CMD_COUNTS_VALUES: sendForDialog( ((Integer)args[0]).intValue(), - XwJNI.server_formatDictCounts( m_jniGamePtr, 3 ) + XwJNI.server_formatDictCounts( m_jniGamePtr.ptr(), 3 ) ); break; case CMD_REMAINING: sendForDialog( ((Integer)args[0]).intValue(), - XwJNI.board_formatRemainingTiles( m_jniGamePtr ) + XwJNI.board_formatRemainingTiles( m_jniGamePtr.ptr() ) ); break; case CMD_RESEND: int nSent = - XwJNI.comms_resendAll( m_jniGamePtr, + XwJNI.comms_resendAll( m_jniGamePtr.ptr(), ((Boolean)args[0]).booleanValue(), ((Boolean)args[1]).booleanValue() ); if ( ((Boolean)args[2]).booleanValue() ) { @@ -536,14 +537,14 @@ public class JNIThread extends Thread { // break; case CMD_HISTORY: - boolean gameOver = XwJNI.server_getGameIsOver( m_jniGamePtr ); + boolean gameOver = XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() ); sendForDialog( ((Integer)args[0]).intValue(), - XwJNI.model_writeGameHistory( m_jniGamePtr, + XwJNI.model_writeGameHistory( m_jniGamePtr.ptr(), gameOver ) ); break; case CMD_FINAL: - if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) { + if ( XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() ) ) { handle( JNICmd.CMD_POST_OVER ); } else { Message.obtain( m_handler, QUERY_ENDGAME ).sendToTarget(); @@ -551,35 +552,35 @@ public class JNIThread extends Thread { break; case CMD_ENDGAME: - XwJNI.server_endGame( m_jniGamePtr ); + XwJNI.server_endGame( m_jniGamePtr.ptr() ); draw = true; break; case CMD_POST_OVER: - if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) { + if ( XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() ) ) { boolean auto = 0 < args.length && ((Boolean)args[0]).booleanValue(); int titleID = auto? R.string.summary_gameover : R.string.finalscores_title; - String text = XwJNI.server_writeFinalScores( m_jniGamePtr ); + String text = XwJNI.server_writeFinalScores( m_jniGamePtr.ptr() ); Message.obtain( m_handler, GAME_OVER, titleID, 0, text ) .sendToTarget(); } break; case CMD_SENDCHAT: - XwJNI.board_sendChat( m_jniGamePtr, (String)args[0] ); + XwJNI.board_sendChat( m_jniGamePtr.ptr(), (String)args[0] ); break; case CMD_NETSTATS: sendForDialog( ((Integer)args[0]).intValue(), - XwJNI.comms_getStats( m_jniGamePtr ) + XwJNI.comms_getStats( m_jniGamePtr.ptr() ) ); break; case CMD_TIMER_FIRED: - draw = XwJNI.timerFired( m_jniGamePtr, + draw = XwJNI.timerFired( m_jniGamePtr.ptr(), ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue() ); @@ -603,7 +604,7 @@ public class JNIThread extends Thread { } // for if ( m_saveOnStop ) { - XwJNI.comms_stop( m_jniGamePtr ); + XwJNI.comms_stop( m_jniGamePtr.ptr() ); save_jni(); } else { DbgUtils.logf( "JNIThread.run(): exiting without saving" ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java index 9a845f117..fb683c6be 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java @@ -22,6 +22,8 @@ package org.eehouse.android.xw4.jni; import android.graphics.Rect; +import junit.framework.Assert; + import org.eehouse.android.xw4.DbgUtils; import org.eehouse.android.xw4.NetLaunchInfo; import org.eehouse.android.xw4.Utils; @@ -30,6 +32,31 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; // Collection of native methods and a bit of state public class XwJNI { + public static class GamePtr { + private int m_ptr = 0; + + private GamePtr( int ptr ) { m_ptr = ptr; } + + public int ptr() { Assert.assertTrue( 0 != m_ptr ); return m_ptr; } + + // 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() + { + if ( 0 != m_ptr ) { + game_dispose( ptr() ); + m_ptr = 0; + } + } + + // @Override + public void finalize() throws java.lang.Throwable + { + Assert.assertTrue( 0 == m_ptr ); + super.finalize(); + } + } + private static XwJNI s_JNI = null; private static XwJNI getJNI() { @@ -102,14 +129,16 @@ public class XwJNI { public static native String comms_getUUID(); // Game methods - public static int initJNI( long rowid ) + public static GamePtr initJNI( long rowid ) { int seed = Utils.nextRandomInt(); String tag = String.format( "%d", rowid ); - return initJNI( getJNI().m_ptr, seed, tag ); + int ptr = initJNI( getJNI().m_ptr, seed, tag ); + GamePtr result = 0 == ptr ? null : new GamePtr( ptr ); + return result; } - public static int initJNI() + public static GamePtr initJNI() { return initJNI( 0 ); } @@ -214,7 +243,7 @@ public class XwJNI { // String dictName, // byte[] dictBytes, // String dictPath ); - public static native void game_dispose( int gamePtr ); + private static native void game_dispose( int gamePtr ); // Board methods public static native void board_setDraw( int gamePtr, DrawCtx draw );