memset(p, 0, sizeof(JNIEnvCache));
}
+/**
+ State for binding Java-side busy handlers.
+*/
+typedef struct {
+ JNIEnv * env; /* env registered from */;
+ jobject jObj /* BusyHandlerJni instance */;
+ jclass klazz /* jObj's class */;
+ jmethodID jmidxCallback;
+} BusyHandlerJni;
+
+
/**
Per-(sqlite3*) state for bindings which do not have their own
finalizer functions, e.g. tracing and commit/rollback hooks. This
data (since they can(?) hypothetically be set via multiple JNIEnv
objects)?
*/
-typedef struct PerDbState PerDbState;
-struct PerDbState {
+typedef struct PerDbStateJni PerDbStateJni;
+struct PerDbStateJni {
JNIEnv *env;
sqlite3 * pDb;
- PerDbState * pNext;
- PerDbState * pPrev;
+ PerDbStateJni * pNext;
+ PerDbStateJni * pPrev;
struct {
jobject jObj;
jmethodID midCallback;
jobject jObj;
jmethodID midCallback;
} rollbackHook;
+ BusyHandlerJni busyHandler;
};
static struct {
JavaVM * jvm;
struct JNIEnvCache envCache;
struct {
- PerDbState * aUsed;
- PerDbState * aFree;
+ PerDbStateJni * aUsed;
+ PerDbStateJni * aFree;
} perDb;
} S3Global;
static void s3jni_free(void * p){
if(p) sqlite3_free(p);
}
+
+/**
+ Clears s's state, releasing any Java references. Before doing so,
+ it calls s's xDestroy() method, ignoring the lack of that method or
+ any exceptions it throws. This is a no-op of s has no current
+ state.
+*/
+static void BusyHandlerJni_clear(BusyHandlerJni * const s){
+ if(s->jObj){
+ JNIEnv * const env = s->env;
+ const jmethodID method =
+ (*env)->GetMethodID(env, s->klazz, "xDestroy", "()V");
+ if(method){
+ (*env)->CallVoidMethod(env, s->jObj, method);
+ EXCEPTION_IGNORE;
+ }else{
+ EXCEPTION_CLEAR;
+ }
+ UNREF_G(s->jObj);
+ UNREF_G(s->klazz);
+ memset(s, 0, sizeof(BusyHandlerJni));
+ }
+}
+
+/**
+ Initializes s to wrap BusyHandlerJni-type object jObject, clearning
+ any current state of s beforehand. Returns 0 on success, non-0 on
+ error. On error, s's state is cleared.
+*/
+static int BusyHandlerJni_init(JNIEnv * const env, BusyHandlerJni * const s,
+ jobject jObj){
+ const char * zSig = "(I)I" /* callback signature */;
+ if(s->jObj) BusyHandlerJni_clear(s);
+ s->env = env;
+ s->jObj = REF_G(jObj);
+ s->klazz = REF_G((*env)->GetObjectClass(env, jObj));
+ s->jmidxCallback = (*env)->GetMethodID(env, s->klazz, "xCallback", zSig);
+ IFTHREW {
+ BusyHandlerJni_clear(s);
+ return SQLITE_ERROR;
+ }
+ return 0;
+}
+
/**
- Extracts the new PerDbState instance from the free-list, or
+ Extracts the new PerDbStateJni instance from the free-list, or
allocates one if needed, associats it with pDb, and returns.
Returns NULL on OOM.
*/
-static PerDbState * PerDbState_alloc(JNIEnv *env, sqlite3 *pDb){
- PerDbState * rv;
+static PerDbStateJni * PerDbStateJni_alloc(JNIEnv *env, sqlite3 *pDb){
+ PerDbStateJni * rv;
assert( pDb );
if(S3Global.perDb.aFree){
rv = S3Global.perDb.aFree;
rv->pNext = 0;
}
}else{
- rv = s3jni_malloc(env, sizeof(PerDbState));
+ rv = s3jni_malloc(env, sizeof(PerDbStateJni));
if(rv){
- memset(rv, 0, sizeof(PerDbState));
- rv->pNext = S3Global.perDb.aUsed;
- S3Global.perDb.aUsed = rv;
- if(rv->pNext){
- assert(!rv->pNext->pPrev);
- rv->pNext->pPrev = rv;
- }
+ memset(rv, 0, sizeof(PerDbStateJni));
}
}
if(rv){
+ rv->pNext = S3Global.perDb.aUsed;
+ S3Global.perDb.aUsed = rv;
+ if(rv->pNext){
+ assert(!rv->pNext->pPrev);
+ rv->pNext->pPrev = rv;
+ }
rv->pDb = pDb;
rv->env = env;
}
Clears s's state and moves it to the free-list.
*/
FIXME_THREADING
-static void PerDbState_set_aside(PerDbState *s){
+static void PerDbStateJni_set_aside(PerDbStateJni *s){
if(s){
JNIEnv * const env = s->env;
assert(s->pDb && "Else this object is already in the free-list.");
UNREF_G(s->progress.jObj);
UNREF_G(s->commitHook.jObj);
UNREF_G(s->rollbackHook.jObj);
- s->env = 0;
- s->pDb = 0;
- s->pPrev = 0;
+ BusyHandlerJni_clear(&s->busyHandler);
+ memset(s, 0, sizeof(PerDbStateJni));
s->pNext = S3Global.perDb.aFree;
S3Global.perDb.aFree = s;
}
}
+static void PerDbStateJni_dump(PerDbStateJni *s){
+ MARKER(("PerDbStateJni->env @ %p\n", s->env));
+ MARKER(("PerDbStateJni->pDb @ %p\n", s->pDb));
+ MARKER(("PerDbStateJni->trace.jObj @ %p\n", s->trace.jObj));
+ MARKER(("PerDbStateJni->progress.jObj @ %p\n", s->progress.jObj));
+ MARKER(("PerDbStateJni->commitHook.jObj @ %p\n", s->commitHook.jObj));
+ MARKER(("PerDbStateJni->rollbackHook.jObj @ %p\n", s->rollbackHook.jObj));
+ MARKER(("PerDbStateJni->busyHandler.env @ %p\n", s->busyHandler.env));
+ MARKER(("PerDbStateJni->busyHandler.jObj @ %p\n", s->busyHandler.jObj));
+ MARKER(("PerDbStateJni->env @ %p\n", s->env));
+}
+
/**
- Returns the PerDbState object for the given db. If allocIfNeeded is
+ Returns the PerDbStateJni object for the given db. If allocIfNeeded is
true then a new instance will be allocated if no mapping currently
exists, else NULL is returned if no mapping is found.
*/
FIXME_THREADING
-static PerDbState * PerDbState_for_db(JNIEnv *env, sqlite3 *pDb, int allocIfNeeded){
- PerDbState * s = S3Global.perDb.aUsed;
+static PerDbStateJni * PerDbStateJni_for_db(JNIEnv *env, sqlite3 *pDb, int allocIfNeeded){
+ PerDbStateJni * s = S3Global.perDb.aUsed;
for( ; s; s = s->pNext){
if(s->pDb == pDb) return s;
}
- if(allocIfNeeded) s = PerDbState_alloc(env, pDb);
+ if(allocIfNeeded) s = PerDbStateJni_alloc(env, pDb);
return s;
}
Cleans up and frees all state in S3Global.perDb.
*/
FIXME_THREADING
-static void PerDbState_free_all(void){
- PerDbState * pS = S3Global.perDb.aUsed;
- PerDbState * pSNext = 0;
+static void PerDbStateJni_free_all(void){
+ PerDbStateJni * pS = S3Global.perDb.aUsed;
+ PerDbStateJni * pSNext = 0;
for( ; pS; pS = pSNext ){
pSNext = pS->pNext;
- PerDbState_set_aside(pS);
+ PerDbStateJni_set_aside(pS);
assert(pSNext ? !pSNext->pPrev : 1);
}
assert( 0==S3Global.perDb.aUsed );
return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n);
}
-JDECL(jint,1busy_1timeout)(JENV_JSELF, jobject pDb, jint ms){
- return sqlite3_busy_timeout(PtrGet_sqlite3(pDb), (int)ms);
+static int s3jni_busy_handler(void* pState, int n){
+ PerDbStateJni * const pS = (PerDbStateJni *)pState;
+ int rc = 0;
+ if( pS->busyHandler.jObj ){
+ JNIEnv * const env = pS->env;
+ rc = (*env)->CallIntMethod(env, pS->busyHandler.jObj,
+ pS->busyHandler.jmidxCallback, (jint)n);
+ }
+ return rc;
+}
+
+JDECL(jint,1busy_1handler)(JENV_JSELF, jobject jDb, jobject jBusy){
+ sqlite3 * const pDb = PtrGet_sqlite3(jDb);
+ PerDbStateJni * const pS = PerDbStateJni_for_db(env, pDb, 1);
+ int rc;
+ if(!pS) return (jint)SQLITE_NOMEM;
+ if(jBusy){
+ if(pS->busyHandler.jObj &&
+ (*env)->IsSameObject(env, pS->busyHandler.jObj, jBusy)){
+ /* Same object - this is a no-op. */
+ return 0;
+ }
+ rc = BusyHandlerJni_init(env, &pS->busyHandler, jBusy);
+ if(rc){
+ assert(!pS->busyHandler.jObj);
+ return (jint)rc;
+ }
+ assert(pS->busyHandler.jObj && pS->busyHandler.klazz);
+ assert( (*env)->IsSameObject(env, pS->busyHandler.jObj, jBusy) );
+ }else{
+ BusyHandlerJni_clear(&pS->busyHandler);
+ }
+ return jBusy
+ ? sqlite3_busy_handler(pDb, s3jni_busy_handler, pS)
+ : sqlite3_busy_handler(pDb, 0, 0);
+}
+
+JDECL(jint,1busy_1timeout)(JENV_JSELF, jobject jDb, jint ms){
+ sqlite3* const pDb = PtrGet_sqlite3(jDb);
+ PerDbStateJni * const pS = PerDbStateJni_for_db(env, pDb, 0);
+ if( pS && pS->busyHandler.jObj ){
+ BusyHandlerJni_clear(&pS->busyHandler);
+ }
+ return sqlite3_busy_timeout(pDb, (int)ms);
}
/**
static jint s3jni_close_db(JNIEnv *env, jobject jDb, int version){
sqlite3 * pDb;
int rc = 0;
- PerDbState * pS;
+ PerDbStateJni * pS = 0;
assert(version == 1 || version == 2);
+ if(0){
+ PerDbStateJni * s = S3Global.perDb.aUsed;
+ for( ; s; s = s->pNext){
+ PerDbStateJni_dump(s);
+ }
+ }
pDb = PtrGet_sqlite3(jDb);
if(!pDb) return rc;
- pS = PerDbState_for_db(env, pDb, 0);
+ pS = PerDbStateJni_for_db(env, pDb, 0);
rc = 1==version ? (jint)sqlite3_close(pDb) : (jint)sqlite3_close_v2(pDb);
- if(pS) PerDbState_set_aside(pS)
+ if(pS) PerDbStateJni_set_aside(pS)
/* MUST come after close() because of pS->trace. */;
setNativePointer(env, jDb, 0, ClassNames.sqlite3);
return (jint)rc;
}
JDECL(jint,1shutdown)(JENV_JSELF){
- PerDbState_free_all();
+ PerDbStateJni_free_all();
JNIEnvCache_clear(&S3Global.envCache);
/* Do not clear S3Global.jvm: it's legal to call
sqlite3_initialize() again to restart the lib. */
}
static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
- PerDbState * const ps = (PerDbState *)pC;
+ PerDbStateJni * const ps = (PerDbStateJni *)pC;
JNIEnv * const env = ps->env;
jobject jX = NULL;
JNIEnvCacheLine * const pEcl = S3Global_env_cache(env);
JDECL(jint,1trace_1v2)(JENV_JSELF,jobject jDb, jint traceMask, jobject jTracer){
sqlite3 * const pDb = PtrGet_sqlite3(jDb);
- PerDbState * ps;
+ PerDbStateJni * ps;
jclass klazz;
if( !traceMask || !jTracer ){
return (jint)sqlite3_trace_v2(pDb, 0, 0, 0);
}
- ps = PerDbState_for_db(env, pDb, 1);
+ ps = PerDbStateJni_for_db(env, pDb, 1);
if(!ps) return SQLITE_NOMEM;
klazz = (*env)->GetObjectClass(env, jTracer);
ps->trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback",
-C Replace\ssome\swww:\sinterwiki\sreferences\sin\sthe\sJNI\sreadme\swith\stheir\sfull\sURLs\sto\smake\sthe\sdoc\smore\sportable.
-D 2023-07-27T20:32:16.722
+C Bind\ssqlite3_busy_handler().\sCorrect\smapping\sof\spointers\sfor,\sand\scleanup\sof,\sJNI-level\sper-db\sstate.
+D 2023-07-27T22:05:39.096
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d
F ext/jni/README.md 042762dbf047667783a5bd0aec303535140f302debfbd259c612edf856661623
-F ext/jni/src/c/sqlite3-jni.c 55bf5624beee849b1c063bf929e6066dc95437564c3212d30e672280bec45da8
-F ext/jni/src/c/sqlite3-jni.h ef862321bb153135472ebe6be6df9db3e47448ae3ef6bb3cb7953c54971efcf8
+F ext/jni/src/c/sqlite3-jni.c 8274a016b5049651ca016486639f90abf050020cf6c880b5273f4e15b29d1ffc
+F ext/jni/src/c/sqlite3-jni.h c9bb150a38dce09cc2794d5aac8fa097288d9946fbb15250fd0a23c31957f506
+F ext/jni/src/org/sqlite/jni/BusyHandler.java aa7574dcf08500ab2334a0ce09c24593374db89815d76fee16da09641c1e32ce
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 70dc7bc41f80352ff3d4331e2e24f45fcd23353b3641e2f68a81bd8262215861
F ext/jni/src/org/sqlite/jni/OutputPointer.java 08a752b58a33696c5eaf0eb9361a0966b188dec40f4a3613eb133123951f6c5f
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5a1d7b2607eb2ef596fcf4492a49d1b3a5bdea3af9918e11716831ffd2f02284
F ext/jni/src/org/sqlite/jni/SQLFunction.java 2f5d197f6c7d73b6031ba1a19598d7e3eee5ebad467eeee62c72e585bd6556a5
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d588c88c17290f5b0d1e4e2a1ea68cf9acab40891c98e08203f1b90ac2aaf8dd
-F ext/jni/src/org/sqlite/jni/Tester1.java 512e545357ce1a5788b250395f2b198ae862f915aee1a8b7b8fae4620d0cfc8d
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 3582b30c0fb1cb39e25b9069fe8c9e2fe4f2659f4d38437b610e46143e163610
+F ext/jni/src/org/sqlite/jni/Tester1.java 1b5f638c9efa0a18579fca5172f4514cd73c8202eacf002fbcc2726efe67fcf0
F ext/jni/src/org/sqlite/jni/Tracer.java c2fe1eba4a76581b93b375a7b95ab1919e5ae60accfb06d6beb067b033e9bae1
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
F ext/jni/src/org/sqlite/jni/sqlite3.java c7d0500c7269882243aafb41425928d094b2fcbdbc2fd1caffc276871cd3fae3
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 0514fd340ae15a95760d50c747d6fb9eae5109cb5045eeabc2bc199be0a5ae35
-R fbc3a722616dae826b754c2990ee20ad
+P 63ce0c9bdde210cf2f8b6099ae5c73caac18e6debc13c2f77090b77f3de72beb
+R c101ee50ca81ac0c17b72552c1b109f1
U stephan
-Z 82e6c4339ed3624b75f36686218f69e5
+Z 3865f2d2a0d46095d9527e301bfc90cc
# Remove this line to create a well-formed Fossil manifest.