mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-15 20:48:00 +01:00
fixing refcounting and adding logging
Seeing the occasional crash and trying to plug leaks that might be contributing. I think I fixed the Gameptr.finalize() assertions I've been seeing forever but the thread match assertion remains. Logs will help catch that.
This commit is contained in:
parent
7f8b3c1820
commit
d5ae8356b4
5 changed files with 44 additions and 34 deletions
|
@ -124,7 +124,7 @@ public class BoardDelegate extends DelegateBase
|
||||||
private boolean m_haveInvited = false;
|
private boolean m_haveInvited = false;
|
||||||
private boolean m_showedReInvite;
|
private boolean m_showedReInvite;
|
||||||
private boolean m_overNotShown;
|
private boolean m_overNotShown;
|
||||||
private boolean m_dropOnDismiss;
|
private boolean m_dropRelayOnDismiss;
|
||||||
private DBAlert m_inviteAlert;
|
private DBAlert m_inviteAlert;
|
||||||
private boolean m_haveStartedShowing;
|
private boolean m_haveStartedShowing;
|
||||||
|
|
||||||
|
@ -1146,7 +1146,7 @@ public class BoardDelegate extends DelegateBase
|
||||||
boolean handled = true;
|
boolean handled = true;
|
||||||
switch ( action ) {
|
switch ( action ) {
|
||||||
case ENABLE_RELAY_DO_OR:
|
case ENABLE_RELAY_DO_OR:
|
||||||
m_dropOnDismiss = true;
|
m_dropRelayOnDismiss = true;
|
||||||
break;
|
break;
|
||||||
case DROP_SMS_ACTION:
|
case DROP_SMS_ACTION:
|
||||||
dropConViaAndRestart(CommsConnType.COMMS_CONN_SMS);
|
dropConViaAndRestart(CommsConnType.COMMS_CONN_SMS);
|
||||||
|
@ -1169,7 +1169,7 @@ public class BoardDelegate extends DelegateBase
|
||||||
boolean handled = true;
|
boolean handled = true;
|
||||||
switch ( action ) {
|
switch ( action ) {
|
||||||
case ENABLE_RELAY_DO_OR:
|
case ENABLE_RELAY_DO_OR:
|
||||||
if ( m_dropOnDismiss ) {
|
if ( m_dropRelayOnDismiss ) {
|
||||||
postDelayed( new Runnable() {
|
postDelayed( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -2466,7 +2466,7 @@ public class BoardDelegate extends DelegateBase
|
||||||
}
|
}
|
||||||
if ( m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
|
if ( m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
|
||||||
if ( !RelayService.relayEnabled( m_activity ) ) {
|
if ( !RelayService.relayEnabled( m_activity ) ) {
|
||||||
m_dropOnDismiss = false;
|
m_dropRelayOnDismiss = false;
|
||||||
String msg = getString( R.string.warn_relay_disabled )
|
String msg = getString( R.string.warn_relay_disabled )
|
||||||
+ "\n\n" + getString( R.string.warn_relay_remove );
|
+ "\n\n" + getString( R.string.warn_relay_remove );
|
||||||
makeConfirmThenBuilder( msg, Action.ENABLE_RELAY_DO_OR )
|
makeConfirmThenBuilder( msg, Action.ENABLE_RELAY_DO_OR )
|
||||||
|
|
|
@ -216,6 +216,7 @@ public class JNIThread extends Thread implements AutoCloseable {
|
||||||
// Assert.assertNull( m_jniGamePtr ); // fired!!
|
// Assert.assertNull( m_jniGamePtr ); // fired!!
|
||||||
if ( null != m_jniGamePtr ) {
|
if ( null != m_jniGamePtr ) {
|
||||||
Log.d( TAG, "configure(): m_jniGamePtr not null; that ok?" );
|
Log.d( TAG, "configure(): m_jniGamePtr not null; that ok?" );
|
||||||
|
m_jniGamePtr.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.graphics.Rect;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.Assert;
|
import org.eehouse.android.xw4.Assert;
|
||||||
|
import org.eehouse.android.xw4.BuildConfig;
|
||||||
import org.eehouse.android.xw4.Log;
|
import org.eehouse.android.xw4.Log;
|
||||||
import org.eehouse.android.xw4.NetLaunchInfo;
|
import org.eehouse.android.xw4.NetLaunchInfo;
|
||||||
import org.eehouse.android.xw4.Utils;
|
import org.eehouse.android.xw4.Utils;
|
||||||
|
@ -78,6 +79,8 @@ public class XwJNI {
|
||||||
game_dispose( this ); // will crash if haveEnv fails
|
game_dispose( this ); // will crash if haveEnv fails
|
||||||
m_ptr = 0;
|
m_ptr = 0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Assert.assertTrue( m_refCount > 0 || !BuildConfig.DEBUG );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +93,10 @@ public class XwJNI {
|
||||||
// @Override
|
// @Override
|
||||||
public void finalize() throws java.lang.Throwable
|
public void finalize() throws java.lang.Throwable
|
||||||
{
|
{
|
||||||
if ( 0 != m_ptr ) {
|
if ( BuildConfig.DEBUG && (0 != m_refCount || 0 != m_ptr) ) {
|
||||||
Log.e( TAG, "ptr not cleared; creator: %s", mStack );
|
Log.e( TAG, "finalize(): called prematurely: refCount: %d"
|
||||||
|
+ "; ptr: %d; creator: %s", m_refCount, m_ptr, mStack );
|
||||||
}
|
}
|
||||||
Assert.assertTrue( 0 == m_refCount );
|
|
||||||
Assert.assertTrue( 0 == m_ptr );
|
|
||||||
super.finalize();
|
super.finalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,12 +201,11 @@ public class XwJNI {
|
||||||
CommonPrefs cp, TransportProcs procs )
|
CommonPrefs cp, TransportProcs procs )
|
||||||
|
|
||||||
{
|
{
|
||||||
GamePtr gamePtr = initGameJNI( rowid );
|
GamePtr gamePtr = initGameJNI( rowid ).retain();
|
||||||
if ( game_makeFromStream( gamePtr, stream, gi, dictNames, dictBytes,
|
if ( ! game_makeFromStream( gamePtr, stream, gi, dictNames, dictBytes,
|
||||||
dictPaths, langName, util, draw,
|
dictPaths, langName, util, draw,
|
||||||
cp, procs ) ) {
|
cp, procs ) ) {
|
||||||
gamePtr.retain();
|
gamePtr.release();
|
||||||
} else {
|
|
||||||
gamePtr = null;
|
gamePtr = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,15 @@
|
||||||
#include "jniutlswrapper.h"
|
#include "jniutlswrapper.h"
|
||||||
#include "paths.h"
|
#include "paths.h"
|
||||||
|
|
||||||
|
#define LOG_MAPPING
|
||||||
|
// #define LOG_MAPPING_ALL
|
||||||
|
|
||||||
typedef struct _EnvThreadEntry {
|
typedef struct _EnvThreadEntry {
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
pthread_t owner;
|
pthread_t owner;
|
||||||
|
#ifdef LOG_MAPPING
|
||||||
|
const char* ownerFunc;
|
||||||
|
#endif
|
||||||
} EnvThreadEntry;
|
} EnvThreadEntry;
|
||||||
|
|
||||||
struct _EnvThreadInfo {
|
struct _EnvThreadInfo {
|
||||||
|
@ -89,8 +95,6 @@ releaseMPool( JNIGlobalState* globalState )
|
||||||
# define releaseMPool(s)
|
# define releaseMPool(s)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_MAPPING
|
|
||||||
// #define LOG_MAPPING_ALL
|
|
||||||
|
|
||||||
#define GAMEPTR_IS_OBJECT
|
#define GAMEPTR_IS_OBJECT
|
||||||
#ifdef GAMEPTR_IS_OBJECT
|
#ifdef GAMEPTR_IS_OBJECT
|
||||||
|
@ -119,8 +123,10 @@ countUsed(const EnvThreadInfo* ti)
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAP_THREAD( ti, env ) map_thread_prv( (ti), (env), __func__ )
|
||||||
|
|
||||||
static void
|
static void
|
||||||
map_thread( EnvThreadInfo* ti, JNIEnv* env )
|
map_thread_prv( EnvThreadInfo* ti, JNIEnv* env, const char* caller )
|
||||||
{
|
{
|
||||||
pthread_t self = pthread_self();
|
pthread_t self = pthread_self();
|
||||||
|
|
||||||
|
@ -170,6 +176,7 @@ map_thread( EnvThreadInfo* ti, JNIEnv* env )
|
||||||
firstEmpty->owner = self;
|
firstEmpty->owner = self;
|
||||||
firstEmpty->env = env;
|
firstEmpty->env = env;
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
|
firstEmpty->ownerFunc = caller;
|
||||||
XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__,
|
XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__,
|
||||||
firstEmpty - ti->entries, env, (int)self );
|
firstEmpty - ti->entries, env, (int)self );
|
||||||
XP_LOGF( "%s: num entries USED now %d", __func__, countUsed(ti) );
|
XP_LOGF( "%s: num entries USED now %d", __func__, countUsed(ti) );
|
||||||
|
@ -177,19 +184,19 @@ map_thread( EnvThreadInfo* ti, JNIEnv* env )
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock( &ti->mtxThreads );
|
pthread_mutex_unlock( &ti->mtxThreads );
|
||||||
} /* map_thread */
|
} /* map_thread_prv */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
map_init( MPFORMAL EnvThreadInfo* ti, JNIEnv* env )
|
map_init( MPFORMAL EnvThreadInfo* ti, JNIEnv* env )
|
||||||
{
|
{
|
||||||
pthread_mutex_init( &ti->mtxThreads, NULL );
|
pthread_mutex_init( &ti->mtxThreads, NULL );
|
||||||
MPASSIGN( ti->mpool, mpool );
|
MPASSIGN( ti->mpool, mpool );
|
||||||
map_thread( ti, env );
|
MAP_THREAD( ti, env );
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAP_REMOVE( ti, env ) map_remove((ti), (env), __func__)
|
#define MAP_REMOVE( ti, env ) map_remove_prv((ti), (env), __func__)
|
||||||
static void
|
static void
|
||||||
map_remove( EnvThreadInfo* ti, JNIEnv* env, const char* func )
|
map_remove_prv( EnvThreadInfo* ti, JNIEnv* env, const char* func )
|
||||||
{
|
{
|
||||||
XP_Bool found = false;
|
XP_Bool found = false;
|
||||||
|
|
||||||
|
@ -197,12 +204,13 @@ map_remove( EnvThreadInfo* ti, JNIEnv* env, const char* func )
|
||||||
for ( int ii = 0; !found && ii < ti->nEntries; ++ii ) {
|
for ( int ii = 0; !found && ii < ti->nEntries; ++ii ) {
|
||||||
found = env == ti->entries[ii].env;
|
found = env == ti->entries[ii].env;
|
||||||
if ( found ) {
|
if ( found ) {
|
||||||
XP_ASSERT( pthread_self() == ti->entries[ii].owner );
|
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
XP_LOGF( "%s: UNMAPPED env %p to thread %x (from %s)", __func__,
|
XP_LOGF( "%s: UNMAPPED env %p to thread %x (from %s; mapped by %s)", __func__,
|
||||||
ti->entries[ii].env,
|
ti->entries[ii].env,
|
||||||
(int)ti->entries[ii].owner, func );
|
(int)ti->entries[ii].owner, func,
|
||||||
|
ti->entries[ii].ownerFunc );
|
||||||
#endif
|
#endif
|
||||||
|
XP_ASSERT( pthread_self() == ti->entries[ii].owner );
|
||||||
ti->entries[ii].env = NULL;
|
ti->entries[ii].env = NULL;
|
||||||
ti->entries[ii].owner = 0;
|
ti->entries[ii].owner = 0;
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
|
@ -724,7 +732,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
||||||
{
|
{
|
||||||
jboolean result = false;
|
jboolean result = false;
|
||||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
map_thread( &globalState->ti, env );
|
MAP_THREAD( &globalState->ti, env );
|
||||||
|
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = getMPool( globalState );
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
|
@ -828,7 +836,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepOutbound
|
||||||
{
|
{
|
||||||
jobjectArray result = NULL;
|
jobjectArray result = NULL;
|
||||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
map_thread( &globalState->ti, env );
|
MAP_THREAD( &globalState->ti, env );
|
||||||
|
|
||||||
SMS_CMD cmd = jEnumToInt( env, jCmd );
|
SMS_CMD cmd = jEnumToInt( env, jCmd );
|
||||||
jbyte* data = NULL;
|
jbyte* data = NULL;
|
||||||
|
@ -867,7 +875,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepInbound
|
||||||
|
|
||||||
if ( !!jData ) {
|
if ( !!jData ) {
|
||||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
map_thread( &globalState->ti, env );
|
MAP_THREAD( &globalState->ti, env );
|
||||||
|
|
||||||
int len = (*env)->GetArrayLength( env, jData );
|
int len = (*env)->GetArrayLength( env, jData );
|
||||||
jbyte* data = (*env)->GetByteArrayElements( env, jData, NULL );
|
jbyte* data = (*env)->GetByteArrayElements( env, jData, NULL );
|
||||||
|
@ -906,7 +914,7 @@ struct _JNIState {
|
||||||
MPSLOT; \
|
MPSLOT; \
|
||||||
MPASSIGN( mpool, state->mpool); \
|
MPASSIGN( mpool, state->mpool); \
|
||||||
XP_ASSERT( !!state->globalJNI ); \
|
XP_ASSERT( !!state->globalJNI ); \
|
||||||
map_thread( &state->globalJNI->ti, env ); \
|
MAP_THREAD( &state->globalJNI->ti, env ); \
|
||||||
|
|
||||||
#define XWJNI_START_GLOBALS() \
|
#define XWJNI_START_GLOBALS() \
|
||||||
XWJNI_START() \
|
XWJNI_START() \
|
||||||
|
@ -924,7 +932,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initGameJNI
|
||||||
#endif
|
#endif
|
||||||
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
||||||
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
||||||
map_thread( &state->globalJNI->ti, env );
|
MAP_THREAD( &state->globalJNI->ti, env );
|
||||||
AndGameGlobals* globals = &state->globals;
|
AndGameGlobals* globals = &state->globals;
|
||||||
globals->dutil = state->globalJNI->dutil;
|
globals->dutil = state->globalJNI->dutil;
|
||||||
globals->state = (JNIState*)state;
|
globals->state = (JNIState*)state;
|
||||||
|
@ -1052,16 +1060,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
||||||
dict_unref( dict ); /* game owns it now */
|
dict_unref( dict ); /* game owns it now */
|
||||||
dict_unref_all( &dicts );
|
dict_unref_all( &dicts );
|
||||||
|
|
||||||
|
/* If game_makeFromStream() fails, the platform-side caller still needs to
|
||||||
|
call game_dispose. That requirement's better than having cleanup code
|
||||||
|
in two places. */
|
||||||
if ( result ) {
|
if ( result ) {
|
||||||
XP_ASSERT( 0 != globals->gi->gameID );
|
XP_ASSERT( 0 != globals->gi->gameID );
|
||||||
if ( !!jgi ) {
|
if ( !!jgi ) {
|
||||||
setJGI( env, jgi, globals->gi );
|
setJGI( env, jgi, globals->gi );
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
destroyDraw( &globals->dctx );
|
|
||||||
destroyXportProcs( &globals->xportProcs );
|
|
||||||
destroyUtil( &globals->util );
|
|
||||||
destroyGI( MPPARM(mpool) &globals->gi );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XWJNI_END();
|
XWJNI_END();
|
||||||
|
|
|
@ -2892,6 +2892,8 @@ loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
|
||||||
XP_FALSE );
|
XP_FALSE );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
game_dispose( &cGlobals->game );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
|
Loading…
Add table
Reference in a new issue