From f601f2bf572678434b1e9df8c7027de4e601a3d5 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Mar 2014 19:14:59 -0700 Subject: [PATCH] use dictmgr in java. This requires jni globals that live across boards being opened and that include a mempool dicts will be allocated out of. Seems to work, and to get ref counts as high as 6 or so before they drop down to one when all boards are closed. (The final is held by the dictmgr which won't give it up until the app itself is GC'd) --- xwords4/android/XWords4/jni/Android.mk | 1 + xwords4/android/XWords4/jni/anddict.c | 103 ++++++++++-------- xwords4/android/XWords4/jni/anddict.h | 9 +- xwords4/android/XWords4/jni/utilwrapper.c | 6 +- xwords4/android/XWords4/jni/xwjni.c | 64 +++++------ .../org/eehouse/android/xw4/jni/XwJNI.java | 42 +++++-- 6 files changed, 128 insertions(+), 97 deletions(-) diff --git a/xwords4/android/XWords4/jni/Android.mk b/xwords4/android/XWords4/jni/Android.mk index 0dc8af81b..3259f292b 100644 --- a/xwords4/android/XWords4/jni/Android.mk +++ b/xwords4/android/XWords4/jni/Android.mk @@ -67,6 +67,7 @@ COMMON_SRC_FILES += \ $(COMMON_PATH)/tray.c \ $(COMMON_PATH)/dictnry.c \ $(COMMON_PATH)/dictiter.c \ + $(COMMON_PATH)/dictmgr.c \ $(COMMON_PATH)/mscore.c \ $(COMMON_PATH)/vtabmgr.c \ $(COMMON_PATH)/strutils.c \ diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index f6aebffc5..14c049d43 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -540,7 +540,10 @@ and_dictionary_destroy( DictionaryCtxt* dict ) jobject and_dictionary_getChars( JNIEnv* env, DictionaryCtxt* dict ) { - XP_ASSERT( env == ((AndDictionaryCtxt*)dict)->env ); + /* XP_ASSERT( env == ((AndDictionaryCtxt*)dict)->env ); */ + /* The above is failing now that dictmgr reuses dicts across threads. I + think that's ok, that I didn't have a good reason for having this + assert. But bears watching... */ /* This is cheating: specials will be rep'd as 1,2, etc. But as long as java code wants to ignore them anyway that's ok. Otherwise need to @@ -563,13 +566,12 @@ and_dictionary_make_empty( MPFORMAL JNIEnv* env, JNIUtilCtxt* jniutil ) dict_super_init( (DictionaryCtxt*)anddict ); MPASSIGN( anddict->super.mpool, mpool ); - DictionaryCtxt* result = dict_ref( (DictionaryCtxt*)anddict ); - LOG_RETURNF( "%p", result ); - return result; + LOG_RETURNF( "%p", anddict ); + return (DictionaryCtxt*)anddict; } void -makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, +makeDicts( MPFORMAL JNIEnv *env, DictMgrCtxt* dictMgr, JNIUtilCtxt* jniutil, DictionaryCtxt** dictp, PlayerDicts* dicts, jobjectArray jnames, jobjectArray jdicts, jobjectArray jpaths, jstring jlang ) @@ -586,7 +588,7 @@ makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, NULL : (*env)->GetObjectArrayElement( env, jpaths, ii ); if ( NULL != jdict || NULL != jpath ) { jstring jname = (*env)->GetObjectArrayElement( env, jnames, ii ); - dict = makeDict( MPPARM(mpool) env, jniutil, jname, jdict, + dict = makeDict( MPPARM(mpool) env, dictMgr, jniutil, jname, jdict, jpath, jlang, false ); XP_ASSERT( !!dict ); deleteLocalRefs( env, jdict, jname, DELETE_NO_REF ); @@ -603,63 +605,72 @@ makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, } DictionaryCtxt* -makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname, +makeDict( MPFORMAL JNIEnv *env, DictMgrCtxt* dictMgr, JNIUtilCtxt* jniutil, jstring jname, jbyteArray jbytes, jstring jpath, jstring jlangname, jboolean check ) { jbyte* bytes = NULL; jbyteArray byteArray = NULL; off_t bytesSize = 0; - if ( NULL == jpath ) { - bytesSize = (*env)->GetArrayLength( env, jbytes ); - byteArray = (*env)->NewGlobalRef( env, jbytes ); - bytes = (*env)->GetByteArrayElements( env, byteArray, NULL ); - } else { - const char* path = (*env)->GetStringUTFChars( env, jpath, NULL ); + const char* name = (*env)->GetStringUTFChars( env, jname, NULL ); + AndDictionaryCtxt* anddict = (AndDictionaryCtxt*)dmgr_get( dictMgr, name ); - struct stat statbuf; - if ( 0 == stat( path, &statbuf ) && 0 < statbuf.st_size ) { - int fd = open( path, O_RDONLY ); - if ( fd >= 0 ) { - void* ptr = mmap( NULL, statbuf.st_size, PROT_READ, - MAP_PRIVATE, fd, 0 ); - close( fd ); - if ( MAP_FAILED != ptr ) { - bytes = ptr; - bytesSize = statbuf.st_size; + if ( NULL == anddict ) { + if ( NULL == jpath ) { + bytesSize = (*env)->GetArrayLength( env, jbytes ); + byteArray = (*env)->NewGlobalRef( env, jbytes ); + bytes = (*env)->GetByteArrayElements( env, byteArray, NULL ); + } else { + const char* path = (*env)->GetStringUTFChars( env, jpath, NULL ); + + struct stat statbuf; + if ( 0 == stat( path, &statbuf ) && 0 < statbuf.st_size ) { + int fd = open( path, O_RDONLY ); + if ( fd >= 0 ) { + void* ptr = mmap( NULL, statbuf.st_size, PROT_READ, + MAP_PRIVATE, fd, 0 ); + close( fd ); + if ( MAP_FAILED != ptr ) { + bytes = ptr; + bytesSize = statbuf.st_size; + } } } + (*env)->ReleaseStringUTFChars( env, jpath, path ); } - (*env)->ReleaseStringUTFChars( env, jpath, path ); - } - AndDictionaryCtxt* anddict = NULL; - if ( NULL != bytes ) { - anddict = (AndDictionaryCtxt*) - and_dictionary_make_empty( MPPARM(mpool) env, jniutil ); - anddict->bytes = bytes; - anddict->byteArray = byteArray; - anddict->bytesSize = bytesSize; + if ( NULL != bytes ) { + anddict = (AndDictionaryCtxt*) + and_dictionary_make_empty( MPPARM(mpool) env, jniutil ); + anddict->bytes = bytes; + anddict->byteArray = byteArray; + anddict->bytesSize = bytesSize; - anddict->super.destructor = and_dictionary_destroy; + anddict->super.destructor = and_dictionary_destroy; - /* copy the name */ - anddict->super.name = getStringCopy( MPPARM(mpool) env, jname ); - XP_LOGF( "%s(dict=%p); code=%x; name=%s", __func__, anddict, - anddict->dbgid, anddict->super.name ); - anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname ); + /* copy the name */ + anddict->super.name = copyString( mpool, name ); + XP_LOGF( "%s(dict=%p); code=%x; name=%s", __func__, anddict, + anddict->dbgid, anddict->super.name ); + anddict->super.langName = getStringCopy( MPPARM(mpool) + env, jlangname ); - XP_U32 numEdges; - XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, - bytesSize, &numEdges ); - if ( !parses || (check && !checkSanity( &anddict->super, - numEdges ) ) ) { - and_dictionary_destroy( (DictionaryCtxt*)anddict ); - anddict = NULL; + XP_U32 numEdges; + XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, + bytesSize, &numEdges ); + if ( !parses || (check && !checkSanity( &anddict->super, + numEdges ) ) ) { + and_dictionary_destroy( (DictionaryCtxt*)anddict ); + anddict = NULL; + } } + dmgr_put( dictMgr, name, &anddict->super ); } - return (DictionaryCtxt*)anddict; + (*env)->ReleaseStringUTFChars( env, jname, name ); + + DictionaryCtxt* result = dict_ref( (DictionaryCtxt*)anddict ); /* NULL ok */ + return result; } /* makeDict */ #ifdef DEBUG diff --git a/xwords4/android/XWords4/jni/anddict.h b/xwords4/android/XWords4/jni/anddict.h index 1eac1a52c..5d24c0695 100644 --- a/xwords4/android/XWords4/jni/anddict.h +++ b/xwords4/android/XWords4/jni/anddict.h @@ -22,6 +22,7 @@ #define _ANDDICT_H_ #include "dictnry.h" +#include "dictmgr.h" #include "comtypes.h" #include "jniutlswrapper.h" @@ -29,11 +30,11 @@ void dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes, XP_U16 nBytes, XP_U16 nFaces ); -DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, - jstring jname, jbyteArray bytes, jstring path, - jstring jlang, jboolean check ); +DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, DictMgrCtxt* dictMgr, + JNIUtilCtxt* jniutil, jstring jname, jbyteArray bytes, + jstring path, jstring jlang, jboolean check ); -void makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, +void makeDicts( MPFORMAL JNIEnv *env, DictMgrCtxt* dictMgr, JNIUtilCtxt* jniutil, DictionaryCtxt** dict, PlayerDicts* dicts, jobjectArray jnames, jobjectArray jdicts, jobjectArray jpaths, jstring jlang ); diff --git a/xwords4/android/XWords4/jni/utilwrapper.c b/xwords4/android/XWords4/jni/utilwrapper.c index d406ab7ba..85a1e51b0 100644 --- a/xwords4/android/XWords4/jni/utilwrapper.c +++ b/xwords4/android/XWords4/jni/utilwrapper.c @@ -392,8 +392,10 @@ and_util_makeEmptyDict( XW_UtilCtxt* uc ) #else AndGlobals* globals = (AndGlobals*)uc->closure; AndUtil* andutil = (AndUtil*)uc; - return and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool ) - *andutil->env, globals->jniutil ); + DictionaryCtxt* result = + and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool ) + *andutil->env, globals->jniutil ); + return dict_ref( result ); #endif } diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index 2fc4e1d72..0d2122845 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -30,6 +30,7 @@ #include "strutils.h" #include "dictnry.h" #include "dictiter.h" +#include "dictmgr.h" #include "utilwrapper.h" #include "drawwrapper.h" @@ -42,6 +43,7 @@ /* Globals for the whole game */ typedef struct _JNIGlobalState { JNIEnv* env; + DictMgrCtxt* dictMgr; MPSLOT } JNIGlobalState; @@ -54,6 +56,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals #endif JNIGlobalState* state = (JNIGlobalState*)XP_CALLOC( mpool, sizeof(*state) ); state->env = env; + state->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) ); MPASSIGN( state->mpool, mpool ); LOG_RETURNF( "%p", state ); return (jint)state; @@ -67,6 +70,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals if ( 0 != ptr ) { JNIGlobalState* state = (JNIGlobalState*)ptr; XP_ASSERT( state->env == env ); + dmgr_destroy( state->dictMgr ); #ifdef MEM_DEBUG MemPoolCtx* mpool = state->mpool; #endif @@ -374,16 +378,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1unref JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo -( JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jname, jstring jpath, - jobject jniu, jboolean check, jobject jinfo ) +( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, + jstring jname, jstring jpath, jobject jniu, jboolean check, jobject jinfo ) { jboolean result = false; -#ifdef MEM_DEBUG - MemPoolCtx* mpool = mpool_make(); -#endif - JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &env, jniu ); - DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, jname, - jDictBytes, jpath, NULL, check ); + JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr; + JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) &env, jniu ); + DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr, + jniutil, jname, jDictBytes, jpath, NULL, check ); if ( NULL != dict ) { if ( NULL != jinfo ) { XP_LangCode code = dict_getLangCode( dict ); @@ -397,9 +399,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo } destroyJNIUtil( &jniutil ); -#ifdef MEM_DEBUG - mpool_destroy( mpool ); -#endif return result; } @@ -531,8 +530,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame DictionaryCtxt* dict; PlayerDicts dicts; - makeDicts( MPPARM(state->globalJNI->mpool) env, globals->jniutil, &dict, &dicts, j_names, - j_dicts, j_paths, j_lang ); + makeDicts( MPPARM(state->globalJNI->mpool) env, state->globalJNI->dictMgr, + globals->jniutil, &dict, &dicts, j_names, j_dicts, + j_paths, j_lang ); #ifdef STUBBED_DICT if ( !dict ) { XP_LOGF( "falling back to stubbed dict" ); @@ -588,8 +588,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream globals->util = makeUtil( MPPARM(mpool) &state->env, jutil, globals->gi, globals ); globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu ); - makeDicts( MPPARM(state->globalJNI->mpool) env, globals->jniutil, &dict, - &dicts, jdictNames, jdicts, jpaths, jlang ); + makeDicts( MPPARM(state->globalJNI->mpool) env, state->globalJNI->dictMgr, + globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths, + jlang ); if ( !!jdraw ) { globals->dctx = makeDraw( MPPARM(mpool) &state->env, jdraw ); } @@ -1515,8 +1516,10 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1changeDict jbyteArray jDictBytes, jstring jpath ) { XWJNI_START_GLOBALS(); - DictionaryCtxt* dict = makeDict( MPPARM(state->globalJNI->mpool) env, globals->jniutil, - jname, jDictBytes, jpath, NULL, false ); + DictionaryCtxt* dict = makeDict( MPPARM(state->globalJNI->mpool) env, + state->globalJNI->dictMgr, + globals->jniutil, jname, jDictBytes, + jpath, NULL, false ); game_changeDict( MPPARM(mpool) &state->game, globals->gi, dict ); dict_unref( dict ); setJGI( env, jgi, globals->gi ); @@ -1637,33 +1640,29 @@ typedef struct _DictIterData { JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init -( JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jname, +( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, jstring jname, jstring jpath, jobject jniu ) { jint closure = 0; -#ifdef MEM_DEBUG - MemPoolCtx* mpool = mpool_make(); -#endif - DictIterData* data = XP_CALLOC( mpool, sizeof(*data) ); + JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr; + DictIterData* data = XP_CALLOC( state->mpool, sizeof(*data) ); data->env = env; - JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &data->env, jniu ); - DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, jname, - jDictBytes, jpath, NULL, false ); + JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) &data->env, jniu ); + DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr, + jniutil, jname, jDictBytes, jpath, NULL, + false ); if ( !!dict ) { - data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) ); + data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(state->mpool) ); data->jniutil = jniutil; data->dict = dict; data->depth = 2; #ifdef MEM_DEBUG - data->mpool = mpool; + data->mpool = state->mpool; #endif closure = (int)data; } else { destroyJNIUtil( &jniutil ); - XP_FREE( mpool, data ); -#ifdef MEM_DEBUG - mpool_destroy( mpool ); -#endif + XP_FREE( state->mpool, data ); } return closure; } @@ -1739,9 +1738,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy freeIndices( data ); vtmgr_destroy( MPPARM(mpool) data->vtMgr ); XP_FREE( mpool, data ); -#ifdef MEM_DEBUG - mpool_destroy( mpool ); -#endif } } 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 c9c1fb18f..6410e88aa 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 @@ -52,14 +52,10 @@ public class XwJNI { } // @Override - public void finalize() + public void finalize() throws java.lang.Throwable { cleanGlobals( m_ptr ); - try { - super.finalize(); - } catch ( java.lang.Throwable err ){ - DbgUtils.logf( "%s", err.toString() ); - } + super.finalize(); } // This needs to be called before the first attempt to use the @@ -329,19 +325,35 @@ public class XwJNI { { return m_dictPtr; } + + // @Override + public void finalize() throws java.lang.Throwable + { + release(); + super.finalize(); + } + } public static native boolean dict_tilesAreSame( int dict1, int dict2 ); public static native String[] dict_getChars( int dict ); - public static native boolean dict_getInfo( byte[] dict, String name, - String path, JNIUtils jniu, - boolean check, DictInfo info ); + public static boolean dict_getInfo( byte[] dict, String name, + String path, JNIUtils jniu, + boolean check, DictInfo info ) + { + return dict_getInfo( getJNI().m_ptr, dict, name, path, jniu, + check, info ); + } + public static native int dict_getTileValue( int dictPtr, int tile ); // Dict iterator public final static int MAX_COLS_DICT = 15; // from dictiter.h - public static native int dict_iter_init( byte[] dict, String name, - String path, JNIUtils jniu ); + public static int dict_iter_init( byte[] dict, String name, + String path, JNIUtils jniu ) + { + return dict_iter_init( getJNI().m_ptr, dict, name, path, jniu ); + } public static native void dict_iter_setMinMax( int closure, int min, int max ); public static native void dict_iter_destroy( int closure ); @@ -358,10 +370,18 @@ public class XwJNI { public static native String base64Encode( byte[] in ); public static native byte[] base64Decode( String in ); + // Private methods -- called only here private static native int initGlobals(); private static native void cleanGlobals( int ptr ); private static native int initJNI( int jniState ); 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, + String name, String path, + JNIUtils jniu, boolean check, + DictInfo info ); + private static native int dict_iter_init( int jniState, byte[] dict, + String name, String path, + JNIUtils jniu ); }