#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 {
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;
#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)
};
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. */;
};
/*
/* 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
**
*/
JavaVM * jvm;
+ sqlite3_mutex * mutex;
/*
** Cache of Java refs and method IDs for NativePointerHolder
** subclasses. Initialized on demand.
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. */;
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 ); \
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));*/ \
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 ); \
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
#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
** 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);
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;
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);
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);
int rc = 0;
S3JniHook hook;
- S3JniHook_copy( env,
+ S3JniHook_localdup( env,
isCommit ? &ps->hooks.commit : &ps->hooks.rollback,
&hook);
if( hook.jObj ){
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 */
(*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);
}
#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
}
const int isPre = 0!=pDb;
S3JniHook hook;
- S3JniHook_copy(env, isPre ?
+ S3JniHook_localdup(env, isPre ?
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&ps->hooks.preUpdate
#else
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{
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;
int rc = 0;
S3JniHook hook;
- S3JniHook_copy( env, &ps->hooks.trace, &hook );
+ S3JniHook_localdup( env, &ps->hooks.trace, &hook );
if( !hook.jObj ){
return 0;
}
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),
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);