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

Refactor the busy-handler-specific JNI hook type to use the generic hook type.

FossilOrigin-Name: d9efdc6dd20a34bfdaad5d4bf8e67cce7e35238299eb91e4459d59fda11978a6
This commit is contained in:
stephan
2023-07-31 10:08:36 +00:00
parent 46c46e400a
commit 452108b4fc
3 changed files with 59 additions and 89 deletions

View File

@ -336,24 +336,14 @@ static void JNIEnvCache_clear(JNIEnvCache * p){
typedef struct JniHookState JniHookState; typedef struct JniHookState JniHookState;
struct JniHookState{ struct JniHookState{
jobject jObj /* global ref to Java instance */; jobject jObj /* global ref to Java instance */;
jmethodID midCallback /* callback method */; jmethodID midCallback /* callback method. Signature depends on
jclass klazz /* jObj's class. Not needed by all types. */; jObj's type */;
jclass klazz /* global ref to jObj's class. Only needed
by hooks which have an xDestroy() method,
as lookup of that method is deferred
until the object requires cleanup. */;
}; };
/**
State for binding Java-side callbacks which potentially have an
xDestroy() method. Maintenance reminder: this is different from
JniHookState because of the need to look up the finalizer. TODO:
look into consolidating this with JniHookState, perhaps adding the
jclass member to that object.
*/
typedef struct BusyHandlerJni BusyHandlerJni;
struct BusyHandlerJni{
JniHookState base;
jclass klazz /* jObj's class */;
};
/** /**
Per-(sqlite3*) state for bindings which do not have their own Per-(sqlite3*) state for bindings which do not have their own
finalizer functions, e.g. tracing and commit/rollback hooks. This finalizer functions, e.g. tracing and commit/rollback hooks. This
@ -380,14 +370,14 @@ struct PerDbStateJni {
receive. */; receive. */;
PerDbStateJni * pNext /* Next entry in the available/free list */; PerDbStateJni * pNext /* Next entry in the available/free list */;
PerDbStateJni * pPrev /* Previous entry in the available/free list */; PerDbStateJni * pPrev /* Previous entry in the available/free list */;
JniHookState trace; JniHookState busyHandler;
JniHookState progress;
JniHookState commitHook;
JniHookState rollbackHook;
JniHookState updateHook;
JniHookState collation; JniHookState collation;
JniHookState collationNeeded; JniHookState collationNeeded;
BusyHandlerJni busyHandler; JniHookState commitHook;
JniHookState progress;
JniHookState rollbackHook;
JniHookState trace;
JniHookState updateHook;
}; };
static struct { static struct {
@ -457,21 +447,21 @@ static int s3jni_db_error(sqlite3*db, int err_code, const char *zMsg){
/** /**
Extracts the (void xDestroy()) method from the given jclass and Extracts the (void xDestroy()) method from the given jclass and
applies it to jobj. If jobs is NULL, this is a no-op. If klazz is applies it to jobj. If jObj is NULL, this is a no-op. If klazz is
NULL then it's derived from jobj. The lack of an xDestroy() method NULL then it's derived from jobj. The lack of an xDestroy() method
is silently ignored and any exceptions thrown by the method trigger is silently ignored and any exceptions thrown by the method trigger
a warning to stdout or stderr and then the exception is suppressed. a warning to stdout or stderr and then the exception is suppressed.
*/ */
static void s3jni_call_xDestroy(JNIEnv *env, jobject jobj, jclass klazz){ static void s3jni_call_xDestroy(JNIEnv *env, jobject jObj, jclass klazz){
if(jobj){ if(jObj){
jmethodID method; jmethodID method;
if(!klazz){ if(!klazz){
klazz = (*env)->GetObjectClass(env, jobj); klazz = (*env)->GetObjectClass(env, jObj);
assert(klazz); assert(klazz);
} }
method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V");
if(method){ if(method){
(*env)->CallVoidMethod(env, jobj, method); (*env)->CallVoidMethod(env, jObj, method);
IFTHREW{ IFTHREW{
EXCEPTION_WARN_CALLBACK_THREW; EXCEPTION_WARN_CALLBACK_THREW;
EXCEPTION_CLEAR; EXCEPTION_CLEAR;
@ -482,41 +472,6 @@ static void s3jni_call_xDestroy(JNIEnv *env, jobject jobj, jclass klazz){
} }
} }
/**
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(JNIEnv *env, BusyHandlerJni * const s){
if(s->base.jObj){
s3jni_call_xDestroy(env, s->base.jObj, s->klazz);
UNREF_G(s->base.jObj);
UNREF_G(s->klazz);
memset(s, 0, sizeof(BusyHandlerJni));
}
}
/**
Initializes s to wrap BusyHandlerJni-type object jObject, clearing
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->base.jObj) BusyHandlerJni_clear(env, s);
s->base.jObj = REF_G(jObj);
s->klazz = REF_G((*env)->GetObjectClass(env, jObj));
s->base.midCallback = (*env)->GetMethodID(env, s->klazz, "xCallback", zSig);
IFTHREW {
BusyHandlerJni_clear(env, s);
return SQLITE_ERROR;
}
return 0;
}
/** /**
Fetches the S3Global.envCache row for the given env, allocing Fetches the S3Global.envCache row for the given env, allocing
a row if needed. When a row is allocated, its state is initialized a row if needed. When a row is allocated, its state is initialized
@ -727,13 +682,22 @@ static PerDbStateJni * PerDbStateJni_alloc(JNIEnv *env, sqlite3 *pDb, jobject jD
return rv; return rv;
} }
/**
Removes any Java references from s and clears its state. If
doXDestroy is true and s->klazz and s->jObj are not NULL, s->jObj's
s is passed to s3jni_call_xDestroy() before any references are
cleared. It is legal to call this when the object has no Java
references.
*/
static void JniHookState_unref(JNIEnv * const env, JniHookState * const s, int doXDestroy){ static void JniHookState_unref(JNIEnv * const env, JniHookState * const s, int doXDestroy){
if(doXDestroy && s->klazz && s->jObj){ if(doXDestroy && s->klazz && s->jObj){
s3jni_call_xDestroy(env, s->jObj, s->klazz); s3jni_call_xDestroy(env, s->jObj, s->klazz);
} }
UNREF_G(s->jObj); UNREF_G(s->jObj);
UNREF_G(s->klazz); UNREF_G(s->klazz);
memset(s, 0, sizeof(JniHookState)); s->jObj = 0;
s->klazz = 0;
s->midCallback = 0;
} }
/** /**
@ -762,9 +726,9 @@ static void PerDbStateJni_set_aside(PerDbStateJni * const s){
UNHOOK(updateHook, 0); UNHOOK(updateHook, 0);
UNHOOK(collation, 1); UNHOOK(collation, 1);
UNHOOK(collationNeeded, 1); UNHOOK(collationNeeded, 1);
UNHOOK(busyHandler, 1);
#undef UNHOOK #undef UNHOOK
UNREF_G(s->jDb); UNREF_G(s->jDb);
BusyHandlerJni_clear(env, &s->busyHandler);
memset(s, 0, sizeof(PerDbStateJni)); memset(s, 0, sizeof(PerDbStateJni));
s->pNext = S3Global.perDb.aFree; s->pNext = S3Global.perDb.aFree;
if(s->pNext) s->pNext->pPrev = s; if(s->pNext) s->pNext->pPrev = s;
@ -781,7 +745,7 @@ static void PerDbStateJni_dump(PerDbStateJni *s){
MARKER(("PerDbStateJni->progress.jObj @ %p\n", s->progress.jObj)); MARKER(("PerDbStateJni->progress.jObj @ %p\n", s->progress.jObj));
MARKER(("PerDbStateJni->commitHook.jObj @ %p\n", s->commitHook.jObj)); MARKER(("PerDbStateJni->commitHook.jObj @ %p\n", s->commitHook.jObj));
MARKER(("PerDbStateJni->rollbackHook.jObj @ %p\n", s->rollbackHook.jObj)); MARKER(("PerDbStateJni->rollbackHook.jObj @ %p\n", s->rollbackHook.jObj));
MARKER(("PerDbStateJni->busyHandler.jObj @ %p\n", s->busyHandler.base.jObj)); MARKER(("PerDbStateJni->busyHandler.jObj @ %p\n", s->busyHandler.jObj));
MARKER(("PerDbStateJni->env @ %p\n", s->env)); MARKER(("PerDbStateJni->env @ %p\n", s->env));
} }
@ -1394,34 +1358,42 @@ JDECL(jint,1bind_1zeroblob64)(JENV_JSELF, jobject jpStmt,
static int s3jni_busy_handler(void* pState, int n){ static int s3jni_busy_handler(void* pState, int n){
PerDbStateJni * const ps = (PerDbStateJni *)pState; PerDbStateJni * const ps = (PerDbStateJni *)pState;
int rc = 0; int rc = 0;
if( ps->busyHandler.base.jObj ){ if( ps->busyHandler.jObj ){
JNIEnv * const env = ps->env; JNIEnv * const env = ps->env;
rc = (*env)->CallIntMethod(env, ps->busyHandler.base.jObj, rc = (*env)->CallIntMethod(env, ps->busyHandler.jObj,
ps->busyHandler.base.midCallback, (jint)n); ps->busyHandler.midCallback, (jint)n);
IFTHREW_CLEAR; IFTHREW{
EXCEPTION_WARN_CALLBACK_THREW;
EXCEPTION_CLEAR;
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "busy-handle callback threw.");
}
} }
return rc; return rc;
} }
JDECL(jint,1busy_1handler)(JENV_JSELF, jobject jDb, jobject jBusy){ JDECL(jint,1busy_1handler)(JENV_JSELF, jobject jDb, jobject jBusy){
PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0); PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0);
int rc; int rc = 0;
if(!ps) return (jint)SQLITE_NOMEM; if(!ps) return (jint)SQLITE_NOMEM;
if(jBusy){ if(jBusy){
if(ps->busyHandler.base.jObj && JniHookState * const pHook = &ps->busyHandler;
(*env)->IsSameObject(env, ps->busyHandler.base.jObj, jBusy)){ if(pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy)){
/* Same object - this is a no-op. */ /* Same object - this is a no-op. */
return 0; return 0;
} }
rc = BusyHandlerJni_init(env, &ps->busyHandler, jBusy); JniHookState_unref(env, pHook, 1);
if(rc){ pHook->jObj = REF_G(jBusy);
assert(!ps->busyHandler.base.jObj); pHook->klazz = REF_G((*env)->GetObjectClass(env, jBusy));
return (jint)rc; pHook->midCallback = (*env)->GetMethodID(env, pHook->klazz, "xCallback", "(I)I");
IFTHREW {
JniHookState_unref(env, pHook, 0);
rc = SQLITE_ERROR;
}
if(rc){
return rc;
} }
assert(ps->busyHandler.base.jObj && ps->busyHandler.klazz);
assert( (*env)->IsSameObject(env, ps->busyHandler.base.jObj, jBusy) );
}else{ }else{
BusyHandlerJni_clear(env, &ps->busyHandler); JniHookState_unref(env, &ps->busyHandler, 1);
} }
return jBusy return jBusy
? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps)
@ -1431,9 +1403,7 @@ JDECL(jint,1busy_1handler)(JENV_JSELF, jobject jDb, jobject jBusy){
JDECL(jint,1busy_1timeout)(JENV_JSELF, jobject jDb, jint ms){ JDECL(jint,1busy_1timeout)(JENV_JSELF, jobject jDb, jint ms){
PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0); PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0);
if( ps ){ if( ps ){
if( ps->busyHandler.base.jObj ){ JniHookState_unref(env, &ps->busyHandler, 1);
BusyHandlerJni_clear(env, &ps->busyHandler);
}
return sqlite3_busy_timeout(ps->pDb, (int)ms); return sqlite3_busy_timeout(ps->pDb, (int)ms);
} }
return SQLITE_MISUSE; return SQLITE_MISUSE;

View File

@ -1,5 +1,5 @@
C Refactor\sthe\scollation-specific\sJNI\shook\stype\sto\suse\sthe\sgeneric\shook\stype. C Refactor\sthe\sbusy-handler-specific\sJNI\shook\stype\sto\suse\sthe\sgeneric\shook\stype.
D 2023-07-31T09:45:49.092 D 2023-07-31T10:08:36.744
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -232,7 +232,7 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d
F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a
F ext/jni/src/c/sqlite3-jni.c 31cf7df43d9c7e99431260e9ec0fbc622587e875ab22b3f4f6a262fd77bf16c8 F ext/jni/src/c/sqlite3-jni.c 9dc18b6eec43132aa9a5001bc12ddd29c69513a4c4d04717b4131a16b6782906
F ext/jni/src/c/sqlite3-jni.h 28def286ee305c1c89a43ac5918a6862d985d0534f7ccbbd74df4885d3918b73 F ext/jni/src/c/sqlite3-jni.h 28def286ee305c1c89a43ac5918a6862d985d0534f7ccbbd74df4885d3918b73
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1 F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
@ -2071,8 +2071,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P f4aa2c82882cb6be1fd52977de19fd03c2e38abb857b520f951b32d610972ab6 P 02c1d3b6501fedf3d6e6d1ca60699df268522182c5ba3b49ae8f4691499ef0fc
R 3fb71491075a1e3e33a1832011a5ad65 R 5b96073cb268292054640619b4e4117b
U stephan U stephan
Z 6de14785676dadf1719d78be6df968a1 Z 2c77c8dd9c75c9f80fb562e45a42818f
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
02c1d3b6501fedf3d6e6d1ca60699df268522182c5ba3b49ae8f4691499ef0fc d9efdc6dd20a34bfdaad5d4bf8e67cce7e35238299eb91e4459d59fda11978a6