diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.c | 61 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 26 |
2 files changed, 35 insertions, 52 deletions
diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 93cb975af..bc776ae16 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -669,14 +669,16 @@ static void s3jni_incr( volatile unsigned int * const p ){ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) +#define S3JniMutex_S3JniDb_assertLocker \ + assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define S3JniMutex_S3JniDb_enter \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ - assert( 0==SJG.perDb.locker ); \ + assert( 0==SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \ s3jni_incr( &SJG.metrics.nMutexPerDb ); \ SJG.perDb.locker = env; #define S3JniMutex_S3JniDb_leave \ - assert( env == SJG.perDb.locker ); \ - SJG.perDb.locker = 0; \ + assert( env == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \ + SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) #else /* SQLITE_THREADSAFE==0 */ @@ -1016,19 +1018,18 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, /* ** Internal helper for many hook callback impls. Locks the S3JniDb -** mutex, makes a copy of src into dest, with one change if src->jObj +** mutex, makes a copy of src into dest, with one change: if src->jObj ** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj -** instead of a copy of the prior GLOBAL ref. Then unlocks the +** instead of a copy of the prior GLOBAL ref. Then it unlocks the ** mutex. If dest->jObj is not NULL when this returns then the caller ** is obligated to eventually free the new ref by passing dest->jObj ** to S3JniUnrefLocal(). The dest pointer must NOT be passed to ** S3JniHook_unref(), as that one assumes that dest->jObj is a GLOBAL -** ref. +** ref (it's illegal to try to unref the wrong ref type).. ** ** Background: when running a hook we need a call-local copy lest ** another thread modify the hook while we're running it. That copy -** has to haves its own Java reference, but it need only be -** call-local. +** has to have its own Java reference, but it need only be call-local. */ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, S3JniHook * const dest ){ @@ -1039,13 +1040,12 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, } /* -** Clears s's state and moves it to the free-list. +** Clears s's state and moves it to the free-list. Requires that that the +** caller has locked S3JniGlobal.perDb.mutex. */ -static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ +static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ if( s ){ -#if SQLITE_THREADSAFE - assert( S3JniGlobal.perDb.locker == env ); -#endif + S3JniMutex_S3JniDb_enter; assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); @@ -1075,12 +1075,6 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; - } -} -static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ - if( s ){ - S3JniMutex_S3JniDb_enter; - S3JniDb_set_aside_unlocked(env, s); S3JniMutex_S3JniDb_leave; } } @@ -1168,7 +1162,8 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env, S3NphRef const* pR if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ - pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); + pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, + pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing nativePointer field."); } S3JniMutex_Nph_leave; @@ -1254,6 +1249,10 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ ** a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 ** object, from which the db is fished out. ** +** If the lockMutex argument is true then the S3JniDb mutex is locked +** before starting work, else the caller is required to have locked +** it. +** ** Returns NULL if jDb and pDb are both NULL or if there is no ** matching S3JniDb entry for pDb or the pointer fished out of jDb. */ @@ -2226,9 +2225,8 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( /* Wrapper for sqlite3_close(_v2)(). */ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ int rc = 0; - S3JniDb * ps = 0; + S3JniDb * const ps = S3JniDb_from_java(jDb); assert(version == 1 || version == 2); - ps = S3JniDb_from_java(jDb); if( ps ){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ @@ -2282,6 +2280,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, ps->hooks.collationNeeded.midCallback, ps->jDb, (jint)eTextRep, jName); S3JniIfThrew{ + S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); } S3JniUnrefLocal(jName); @@ -2394,9 +2393,9 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, int rc = 0; S3JniHook hook; - S3JniHook_localdup( env, - isCommit ? &ps->hooks.commit : &ps->hooks.rollback, - &hook); + S3JniHook_localdup( env, isCommit + ? &ps->hooks.commit : &ps->hooks.rollback, + &hook); if( hook.jObj ){ rc = isCommit ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) @@ -3495,8 +3494,7 @@ S3JniApi(sqlite3_result_double(),void,1result_1double)( } S3JniApi(sqlite3_result_error(),void,1result_1error)( - JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, - int eTextRep + JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, int eTextRep ){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); @@ -3504,12 +3502,12 @@ S3JniApi(sqlite3_result_error(),void,1result_1error)( switch( pjBuf ? eTextRep : SQLITE_UTF8 ){ case SQLITE_UTF8: { const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; - sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); + int const n = pjBuf ? (int)baLen : (int)sqlite3Strlen30(zMsg); + sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, n); break; } case SQLITE_UTF16: { - const void *zMsg = pjBuf - ? (const void *)pjBuf : (const void *)zUnspecified; + const void *zMsg = pjBuf; sqlite3_result_error16(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); break; } @@ -3678,13 +3676,12 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( ")I"); S3JniUnrefLocal(klazz); S3JniIfThrew { - S3JniHook_unref(env, pHook, 0); rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "Error setting up Java parts of authorizer hook."); }else{ rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); - if( rc ) S3JniHook_unref(env, pHook, 0); } + if( rc ) S3JniHook_unref(env, pHook, 0); } S3JniMutex_S3JniDb_leave; return rc; diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 4c13286fe..598fa2a01 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -587,7 +587,8 @@ public final class SQLite3Jni { public static native String sqlite3_errstr(int resultCode); /** - Note that the offset values assume UTF-8-encoded SQL. + Note that the returned byte offset values assume UTF-8-encoded + inputs, so won't always match character offsets in Java Strings. */ public static native int sqlite3_error_offset(@NotNull sqlite3 db); @@ -595,23 +596,8 @@ public final class SQLite3Jni { public static native int sqlite3_initialize(); - /** - Design note/FIXME: we have a problem vis-a-vis 'synchronized' - here: we specifically want other threads to be able to cancel a - long-running thread, but this routine requires access to C-side - global state which does not have a mutex. Making this function - synchronized would make it impossible for a long-running job to - be cancelled from another thread. - - The mutexing problem here is not within the core lib or Java, but - within the cached data held by the JNI binding. The cache holds - per-thread state, used by all but a tiny fraction of the JNI - binding layer, and access to that state needs to be - mutex-protected. - */ public static native void sqlite3_interrupt(@NotNull sqlite3 db); - //! See sqlite3_interrupt() for threading concerns. public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db); public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); @@ -907,7 +893,7 @@ public final class SQLite3Jni { a complaint about the invalid argument. */ private static native void sqlite3_result_error( - @NotNull sqlite3_context cx, @Nullable byte[] msg, + @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); @@ -920,12 +906,12 @@ public final class SQLite3Jni { public static void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = (msg+"\0").getBytes(StandardCharsets.UTF_8); + final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_8); sqlite3_result_error(cx, utf8, SQLITE_UTF8); } public static void sqlite3_result_error16( - @NotNull sqlite3_context cx, @Nullable byte[] utf16 + @NotNull sqlite3_context cx, @NotNull byte[] utf16 ){ sqlite3_result_error(cx, utf16, SQLITE_UTF16); } @@ -933,7 +919,7 @@ public final class SQLite3Jni { public static void sqlite3_result_error16( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = (msg+"\0").getBytes(StandardCharsets.UTF_16); + final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_16); sqlite3_result_error(cx, utf8, SQLITE_UTF16); } |