mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-05 20:45:49 +01:00
stop (or at least greatly reduce) leakage of thread->env mappings in
jni, mostly by having jnithread explicitly announce that it's closing. Yuck. This stuff *should* obey a stacking protocol but the callback stuff I'm doing makes me unsure that can work.
This commit is contained in:
parent
a1bbbe3371
commit
56121fdcd4
3 changed files with 61 additions and 18 deletions
|
@ -52,7 +52,6 @@ typedef struct _EnvThreadEntry {
|
|||
struct _EnvThreadInfo {
|
||||
pthread_mutex_t mtxThreads;
|
||||
EnvThreadEntry entries[MAX_ENV_THREADS];
|
||||
XP_U16 nMaps;
|
||||
};
|
||||
|
||||
/* Globals for the whole game */
|
||||
|
@ -70,25 +69,31 @@ map_thread( EnvThreadInfo* ti, JNIEnv* env )
|
|||
pthread_mutex_lock( &ti->mtxThreads );
|
||||
|
||||
XP_Bool found = false;
|
||||
for ( int ii = 0; !found && ii < ti->nMaps; ++ii ) {
|
||||
EnvThreadEntry* firstEmpty = NULL;
|
||||
for ( int ii = 0; !found && ii < VSIZE(ti->entries); ++ii ) {
|
||||
EnvThreadEntry* entry = &ti->entries[ii];
|
||||
found = self == entry->owner;
|
||||
if ( found ) {
|
||||
if ( 0 == entry->owner ) {
|
||||
if ( NULL == firstEmpty ) {
|
||||
firstEmpty = entry;
|
||||
}
|
||||
} else if ( self == entry->owner ) {
|
||||
found = true;
|
||||
if ( env != entry->env ) {
|
||||
/* this DOES happen!!! */
|
||||
XP_LOGF( "%s: replacing env %p with env %p for thread %x",
|
||||
__func__, entry->env, env, (int)self );
|
||||
XP_LOGF( "%s (ti=%p): replacing env %p with env %p for thread %x",
|
||||
__func__, ti, entry->env, env, (int)self );
|
||||
entry->env = env;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !found ) {
|
||||
EnvThreadEntry* entry = &ti->entries[ti->nMaps++];
|
||||
entry->owner = self;
|
||||
entry->env = env;
|
||||
XP_LOGF( "%s: mapped env %p to thread %x", __func__, env, (int)self );
|
||||
XP_ASSERT( ti->nMaps < MAX_ENV_THREADS );
|
||||
XP_ASSERT( !!firstEmpty );
|
||||
firstEmpty->owner = self;
|
||||
firstEmpty->env = env;
|
||||
int indx = firstEmpty - ti->entries;
|
||||
XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__, indx,
|
||||
env, (int)self );
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &ti->mtxThreads );
|
||||
|
@ -101,11 +106,27 @@ map_init( EnvThreadInfo* ti, JNIEnv* env )
|
|||
map_thread( ti, env );
|
||||
}
|
||||
|
||||
static void
|
||||
map_remove( EnvThreadInfo* ti, JNIEnv* env )
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
XP_Bool found = false;
|
||||
for ( int ii = 0; !found && ii < VSIZE(ti->entries); ++ii ) {
|
||||
found = env == ti->entries[ii].env;
|
||||
if ( found ) {
|
||||
XP_LOGF( "%s: clearing out %dth entry (thread %x)", __func__, ii,
|
||||
(int)self );
|
||||
ti->entries[ii].env = NULL;
|
||||
XP_ASSERT( ti->entries[ii].owner = self );
|
||||
ti->entries[ii].owner = 0;
|
||||
}
|
||||
}
|
||||
XP_ASSERT( found );
|
||||
}
|
||||
|
||||
static void
|
||||
map_destroy( EnvThreadInfo* ti )
|
||||
{
|
||||
// XP_ASSERT( 0 == ti->nMaps );
|
||||
XP_LOGF( "%s: had %d threads max", __func__, ti->nMaps );
|
||||
pthread_mutex_destroy( &ti->mtxThreads );
|
||||
}
|
||||
|
||||
|
@ -115,7 +136,7 @@ envForMe( EnvThreadInfo* ti, const char* caller )
|
|||
JNIEnv* result = NULL;
|
||||
pthread_t self = pthread_self();
|
||||
pthread_mutex_lock( &ti->mtxThreads );
|
||||
for ( int ii = 0; ii < ti->nMaps; ++ii ) {
|
||||
for ( int ii = 0; ii < VSIZE(ti->entries); ++ii ) {
|
||||
if ( self == ti->entries[ii].owner ) {
|
||||
result = ti->entries[ii].env;
|
||||
break;
|
||||
|
@ -123,8 +144,7 @@ envForMe( EnvThreadInfo* ti, const char* caller )
|
|||
}
|
||||
pthread_mutex_unlock( &ti->mtxThreads );
|
||||
if( !result ) {
|
||||
XP_LOGF( "no env for %s (thread %x in %d entries)", caller,
|
||||
(int)self, ti->nMaps );
|
||||
XP_LOGF( "no env for %s (thread %x)", caller, (int)self );
|
||||
XP_ASSERT(0);
|
||||
}
|
||||
return result;
|
||||
|
@ -470,6 +490,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
|||
{
|
||||
jboolean result = false;
|
||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
||||
map_thread( &state->ti, env );
|
||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) &state->ti, jniu );
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
||||
jniutil, jname, jDictBytes, jpath,
|
||||
|
@ -571,6 +592,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initJNI
|
|||
return (jint) state;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_envDone
|
||||
( JNIEnv* env, jclass C, int jniGlobalPtr )
|
||||
{
|
||||
JNIGlobalState* globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
||||
map_remove( &globalJNI->ti, env );
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
||||
( JNIEnv* env, jclass C, jint gamePtr, jobject j_gi, jobject j_util,
|
||||
|
@ -618,7 +647,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
|||
} /* makeNewGame */
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
|
||||
( JNIEnv * env, jclass claz, jint gamePtr )
|
||||
( JNIEnv* env, jclass claz, jint gamePtr )
|
||||
{
|
||||
JNIState* state = (JNIState*)gamePtr;
|
||||
#ifdef MEM_DEBUG
|
||||
|
@ -636,6 +665,8 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
|
|||
destroyJNIUtil( &globals->jniutil );
|
||||
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
||||
|
||||
map_remove( &state->globalJNI->ti, env );
|
||||
|
||||
XP_FREE( mpool, state );
|
||||
mpool_destroy( mpool );
|
||||
} /* game_dispose */
|
||||
|
@ -1710,6 +1741,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1sendChat
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct _DictIterData {
|
||||
JNIGlobalState* state;
|
||||
JNIEnv* env;
|
||||
JNIUtilCtxt* jniutil;
|
||||
VTableMgr* vtMgr;
|
||||
|
@ -1729,7 +1761,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
|
|||
{
|
||||
jint closure = 0;
|
||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
||||
map_thread( &state->ti, env );
|
||||
DictIterData* data = XP_CALLOC( state->mpool, sizeof(*data) );
|
||||
data->state = state;
|
||||
data->env = env;
|
||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) &state->ti, jniu );
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
||||
|
@ -1821,6 +1855,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy
|
|||
destroyJNIUtil( &data->jniutil );
|
||||
freeIndices( data );
|
||||
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
|
||||
map_remove( &data->state->ti, env );
|
||||
XP_FREE( mpool, data );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -594,6 +594,7 @@ public class JNIThread extends Thread {
|
|||
} else {
|
||||
DbgUtils.logf( "JNIThread.run(): exiting without saving" );
|
||||
}
|
||||
XwJNI.threadDone();
|
||||
} // run
|
||||
|
||||
public void handleBkgrnd( JNICmd cmd, Object... args )
|
||||
|
|
|
@ -92,6 +92,12 @@ public class XwJNI {
|
|||
return initJNI( getJNI().m_ptr, seed );
|
||||
}
|
||||
|
||||
// hack to allow cleanup of env owned by thread that doesn't open game
|
||||
public static void threadDone()
|
||||
{
|
||||
envDone( getJNI().m_ptr );
|
||||
}
|
||||
|
||||
public static native void game_makeNewGame( int gamePtr,
|
||||
CurGameInfo gi,
|
||||
UtilCtxt util,
|
||||
|
@ -386,8 +392,9 @@ public class XwJNI {
|
|||
|
||||
// Private methods -- called only here
|
||||
private static native int initGlobals();
|
||||
private static native void cleanGlobals( int ptr );
|
||||
private static native void cleanGlobals( int globals );
|
||||
private static native int initJNI( int jniState, int seed );
|
||||
private static native void envDone( int globals );
|
||||
private static native void dict_ref( int dictPtr );
|
||||
private static native void dict_unref( int dictPtr );
|
||||
private static native boolean dict_getInfo( int jniState, byte[] dict,
|
||||
|
|
Loading…
Add table
Reference in a new issue