1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

JNI internal cleanups and docs.

FossilOrigin-Name: b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6
This commit is contained in:
stephan
2023-08-26 11:57:34 +00:00
parent d9cf47e377
commit 3ff458d61e
4 changed files with 119 additions and 82 deletions

View File

@ -233,8 +233,8 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){
#define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR))
#define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR))
/**
Keys for use with S3JniGlobal_nph_cache().
/*
** Key type for use with S3JniGlobal_nph_cache().
*/
typedef struct S3NphRef S3NphRef;
struct S3NphRef {
@ -242,10 +242,11 @@ struct S3NphRef {
const char * const zName /* Full Java name of the class */;
};
/**
Keys for each concrete NativePointerHolder subclass. These are to
be used with S3JniGlobal_nph_cache() and friends. These are
initialized on-demand by S3JniGlobal_nph_cache().
/*
** Cache keys for each concrete NativePointerHolder subclass and
** OutputPointer type. The members are to be used with
** S3JniGlobal_nph_cache() and friends, and each one's member->index
** corresponds to its index in the S3JniGlobal.nph[] array.
*/
static const struct {
const S3NphRef sqlite3;
@ -289,21 +290,14 @@ static const struct {
#undef NREF
};
/* Helpers for jstring and jbyteArray. */
#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT)
enum {
/*
** Size of the NativePointerHolder cache. Need enough space for
** (only) the library's NativePointerHolder types, a fixed count
** known at build-time. If we add more than this a fatal error will
** be triggered with a reminder to increase this. This value needs
** to be exactly the number of entries in the S3NphRefs object. The
** index field of those entries are the keys for this particular
** cache.
** (only) the library's NativePointerHolder and OutputPointer types,
** a fixed count known at build-time. This value needs to be
** exactly the number of S3NphRef entries in the S3NphRefs
** object. The index field of those entries are the keys for this
** particular cache.
*/
S3Jni_NphCache_size = sizeof(S3NphRefs) / sizeof(S3NphRef)
};
@ -315,15 +309,15 @@ enum {
typedef struct S3JniNphClass S3JniNphClass;
struct S3JniNphClass {
volatile const S3NphRef * pRef /* Entry from S3NphRefs. */;
jclass klazz /* global ref to the concrete
** NativePointerHolder subclass represented by
** zClassName */;
volatile jmethodID midCtor /* klazz's no-arg constructor. Used by
** new_NativePointerHolder_object(). */;
volatile jfieldID fidValue /* NativePointerHolder.nativePointer or
** OutputPointer.T.value */;
volatile jfieldID fidAggCtx /* sqlite3_context::aggregateContext. Used only
** by the sqlite3_context binding. */;
jclass klazz /* global ref to the concrete
** NativePointerHolder subclass
** represented by zClassName */;
volatile jmethodID midCtor /* klazz's no-arg constructor. Used by
** new_NativePointerHolder_object(). */;
volatile jfieldID fidValue /* NativePointerHolder.nativePointer or
** OutputPointer.T.value */;
volatile jfieldID fidAggCtx /* sqlite3_context.aggregateContext, used only
** by the sqlite3_context binding. */;
};
/*
@ -337,7 +331,7 @@ struct S3JniHook{
/* We lookup the jObj.xDestroy() method as-needed for contexts which
** have custom finalizers. */
};
#ifndef SQLITE_ENABLE_PREUPDATE_HOOK
#if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) || defined(SQLITE_ENABLE_SQLLOG)
static const S3JniHook S3JniHook_empty = {0,0};
#endif
@ -459,6 +453,7 @@ struct S3JniGlobalType {
**
*/
JavaVM * jvm;
sqlite3_mutex * mutex;
/*
** Cache of Java refs and method IDs for NativePointerHolder
** subclasses. Initialized on demand.
@ -546,6 +541,7 @@ struct S3JniGlobalType {
a S3JniNphClass operation. */;
volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */;
volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */;
volatile unsigned nMutexGlobal /* number of times global mutex was entered. */;
volatile unsigned nDestroy /* xDestroy() calls across all types */;
volatile unsigned nPdbAlloc /* Number of S3JniDb alloced. */;
volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */;
@ -601,6 +597,7 @@ static void s3jni_incr( volatile unsigned int * const p ){
S3JniMutex_Env_assertLocker; \
SJG.envCache.locker = 0; \
sqlite3_mutex_leave( SJG.envCache.mutex )
#define S3JniMutex_Ext_enter \
/*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \
sqlite3_mutex_enter( SJG.autoExt.mutex ); \
@ -612,6 +609,15 @@ static void s3jni_incr( volatile unsigned int * const p ){
sqlite3_mutex_leave( SJG.autoExt.mutex )
#define S3JniMutex_Ext_assertLocker \
assert( env == SJG.autoExt.locker )
#define S3JniMutex_Global_enter \
/*MARKER(("Entering GLOBAL mutex@%p %s.\n", env));*/ \
sqlite3_mutex_enter( SJG.mutex ); \
s3jni_incr(&SJG.metrics.nMutexGlobal);
#define S3JniMutex_Global_leave \
/*MARKER(("Leaving GLOBAL mutex @%p %s.\n", env));*/ \
sqlite3_mutex_leave( SJG.mutex )
#define S3JniMutex_Nph_enter \
S3JniMutex_Env_assertNotLocker; \
/*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \
@ -623,6 +629,7 @@ static void s3jni_incr( volatile unsigned int * const p ){
S3JniMutex_Env_assertLocker; \
SJG.envCache.locker = 0; \
sqlite3_mutex_leave( SJG.envCache.mutex )
#define S3JniMutex_S3JniDb_enter \
sqlite3_mutex_enter( SJG.perDb.mutex ); \
assert( 0==SJG.perDb.locker ); \
@ -633,6 +640,7 @@ static void s3jni_incr( volatile unsigned int * const p ){
assert( env == SJG.perDb.locker ); \
SJG.perDb.locker = 0; \
sqlite3_mutex_leave( SJG.perDb.mutex )
#else /* SQLITE_THREADSAFE==0 */
#define S3JniMutex_Env_assertLocked
#define S3JniMutex_Env_assertLocker
@ -642,17 +650,29 @@ static void s3jni_incr( volatile unsigned int * const p ){
#define S3JniMutex_Ext_assertLocker
#define S3JniMutex_Ext_enter
#define S3JniMutex_Ext_leave
#define S3JniMutex_Global_enter
#define S3JniMutex_Global_leave
#define S3JniMutex_Nph_enter
#define S3JniMutex_Nph_leave
#define S3JniMutex_S3JniDb_enter
#define S3JniMutex_S3JniDb_leave
#endif
#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env)
/* Helpers for jstring and jbyteArray. */
#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT)
/* Fail fatally with an OOM message. */
static inline void s3jni_oom(JNIEnv * const env){
(*env)->FatalError(env, "Out of memory.") /* does not return */;
}
/* Fail fatally if !VAR. */
#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env)
/*
** sqlite3_malloc() proxy which fails fatally on OOM. This should
** only be used for routines which manage global state and have no
@ -969,8 +989,8 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest
** has to haves its own Java reference, but it need only be
** call-local.
*/
static void S3JniHook_copy( JNIEnv * const env, S3JniHook const * const src,
S3JniHook * const dest ){
static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src,
S3JniHook * const dest ){
S3JniMutex_S3JniDb_enter;
*dest = *src;
if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj);
@ -1402,7 +1422,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs,
jint rc = 0;
S3JniHook hook;
S3JniHook_copy(env, &ps->hooks.collation, &hook );
S3JniHook_localdup(env, &ps->hooks.collation, &hook );
if( hook.jObj ){
jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs);
jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL;
@ -2092,7 +2112,7 @@ static int s3jni_busy_handler(void* pState, int n){
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_copy(env, &ps->hooks.busyHandler, &hook );
S3JniHook_localdup(env, &ps->hooks.busyHandler, &hook );
if( hook.jObj ){
rc = (*env)->CallIntMethod(env, hook.jObj,
hook.midCallback, (jint)n);
@ -2230,7 +2250,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_copy(env, &ps->hooks.collationNeeded, &hook );
S3JniHook_localdup(env, &ps->hooks.collationNeeded, &hook );
if( hook.jObj ){
unsigned int const nName = s3jni_utf16_strlen(z16Name);
jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName);
@ -2354,7 +2374,7 @@ static int s3jni_commit_rollback_hook_impl(int isCommit,
int rc = 0;
S3JniHook hook;
S3JniHook_copy( env,
S3JniHook_localdup( env,
isCommit ? &ps->hooks.commit : &ps->hooks.rollback,
&hook);
if( hook.jObj ){
@ -2472,9 +2492,12 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int
jstring jArg1 = 0;
S3JniDeclLocal_env;
S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb);
S3JniHook * const hook = &SJG.hooks.sqllog;
S3JniHook hook = S3JniHook_empty;
if( !ps || !hook->jObj ) return;
if( ps ){
S3JniHook_localdup(env, &SJG.hooks.sqllog, &hook);
}
if( !hook.jObj ) return;
jArg0 = S3JniRefLocal(ps->jDb);
switch( op ){
case 0: /* db opened */
@ -2487,11 +2510,12 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int
(*env)->FatalError(env, "Unhandled 4th arg to SQLITE_CONFIG_SQLLOG.");
break;
}
(*env)->CallVoidMethod(env, hook->jObj, hook->midCallback, jArg0, jArg1, op);
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, jArg0, jArg1, op);
S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback");
S3JniExceptionClear;
}
S3JniUnrefLocal(hook.jObj);
S3JniUnrefLocal(jArg0);
S3JniUnrefLocal(jArg1);
}
@ -2502,41 +2526,40 @@ void sqlite3_init_sqllog(void){
#endif
S3JniApi(sqlite3_config() /* for SQLLOG */,
jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){
jint, 1config__Lorg_sqlite_jni_ConfigSqllogCallback_2)(
JniArgsEnvClass, jobject jLog
){
#ifndef SQLITE_ENABLE_SQLLOG
return SQLITE_MISUSE;
#else
S3JniHook tmpHook;
S3JniHook * const hook = &tmpHook;
S3JniHook * const hookOld = & SJG.hooks.sqllog;
jclass klazz;
S3JniHook * const pHook = &SJG.hooks.sqllog;
int rc = 0;
S3JniMutex_Global_enter;
if( !jLog ){
S3JniHook_unref(env, hookOld, 0);
return 0;
}
if( hookOld->jObj && (*env)->IsSameObject(env, jLog, hookOld->jObj) ){
return 0;
}
klazz = (*env)->GetObjectClass(env, jLog);
hook->midCallback = (*env)->GetMethodID(env, klazz, "call",
"(Lorg/sqlite/jni/sqlite3;"
"Ljava/lang/String;"
"I)V");
S3JniUnrefLocal(klazz);
if( !hook->midCallback ){
S3JniExceptionWarnIgnore;
S3JniHook_unref(env, hook, 0);
return SQLITE_ERROR;
}
hook->jObj = S3JniRefGlobal(jLog);
rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
if( rc ){
S3JniHook_unref(env, hook, 0);
}else{
S3JniHook_unref(env, hookOld, 0);
*hookOld = *hook;
S3JniHook_unref(env, pHook, 0);
}else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){
/* No-op */
}else {
jclass const klazz = (*env)->GetObjectClass(env, jLog);
jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call",
"(Lorg/sqlite/jni/sqlite3;"
"Ljava/lang/String;"
"I)V");
S3JniUnrefLocal(klazz);
if( midCallback ){
rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
if( 0==rc ){
S3JniHook_unref(env, pHook, 0);
pHook->midCallback = midCallback;
pHook->jObj = S3JniRefGlobal(jLog);
}
}else{
S3JniExceptionWarnIgnore;
rc = SQLITE_ERROR;
}
}
S3JniMutex_Global_leave;
return rc;
#endif
}
@ -3080,7 +3103,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
const int isPre = 0!=pDb;
S3JniHook hook;
S3JniHook_copy(env, isPre ?
S3JniHook_localdup(env, isPre ?
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&ps->hooks.preUpdate
#else
@ -3274,7 +3297,7 @@ static int s3jni_progress_handler_impl(void *pP){
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_copy( env, &ps->hooks.progress, &hook );
S3JniHook_localdup( env, &ps->hooks.progress, &hook );
if( hook.jObj ){
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback);
S3JniIfThrew{
@ -3569,7 +3592,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
S3JniHook hook;
int rc = 0;
S3JniHook_copy(env, &ps->hooks.auth, &hook );
S3JniHook_localdup(env, &ps->hooks.auth, &hook );
if( hook.jObj ){
jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0;
jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0;
@ -3759,7 +3782,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
int rc = 0;
S3JniHook hook;
S3JniHook_copy( env, &ps->hooks.trace, &hook );
S3JniHook_localdup( env, &ps->hooks.trace, &hook );
if( !hook.jObj ){
return 0;
}
@ -3973,13 +3996,15 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
SJG.metrics.envCacheMisses,
SJG.metrics.envCacheHits);
printf("Mutex entry:"
"\n\tglobal %u"
"\n\tenv %u"
"\n\tnph inits %u"
"\n\tperDb %u"
"\n\tautoExt %u list accesses"
"\n\tmetrics %u\n",
SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2,
SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt,
SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv,
SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb,
SJG.metrics.nMutexAutoExt,
SJG.metrics.nMetrics);
printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n",
SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb),
@ -4906,6 +4931,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){
S3JniUnrefLocal(klazz);
#endif
SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
s3jni_oom_check( SJG.mutex );
SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
s3jni_oom_check( SJG.envCache.mutex );
SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);

View File

@ -494,6 +494,11 @@ public final class SQLite3Jni {
<p>Others may be added in the future. It returns SQLITE_MISUSE if
given an argument it does not handle.
<p>Note that sqlite3_config() is not threadsafe with regards to
the rest of the library. This must not be called when any other
library APIs are being called.
*/
public static native int sqlite3_config(int op);
@ -504,8 +509,13 @@ public final class SQLite3Jni {
logger. If installation of a logger fails, any previous logger is
retained.
If not built with SQLITE_ENABLE_SQLLOG defined, this returns
<p>If not built with SQLITE_ENABLE_SQLLOG defined, this returns
SQLITE_MISUSE.
<p>Note that sqlite3_config() is not threadsafe with regards to
the rest of the library. This must not be called when any other
library APIs are being called.
*/
public static native int sqlite3_config( @Nullable ConfigSqllogCallback logger );