]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
An initial attempt at protecting the JNI global state via mutexes at the C level...
authorstephan <stephan@noemail.net>
Sun, 13 Aug 2023 09:53:27 +0000 (09:53 +0000)
committerstephan <stephan@noemail.net>
Sun, 13 Aug 2023 09:53:27 +0000 (09:53 +0000)
FossilOrigin-Name: c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4

ext/jni/src/c/sqlite3-jni.c
ext/jni/src/c/sqlite3-jni.h
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
manifest
manifest.uuid

index 6c60aeaf3af8189580a4d28d8dcf4f2ad9072699..4522e05b45705756ea01275d15c7607f2bd56f30 100644 (file)
@@ -498,10 +498,14 @@ static struct {
   struct {
     S3JniEnvCache * aHead /* Linked list of in-use instances */;
     S3JniEnvCache * aFree /* Linked list of free instances */;
+    sqlite3_mutex * mutex /* mutex for aHead and aFree */;
+    void const * locker   /* env mutex is held on this object's behalf */;
   } envCache;
   struct {
     S3JniDb * aUsed  /* Linked list of in-use instances */;
     S3JniDb * aFree  /* Linked list of free instances */;
+    sqlite3_mutex * mutex  /* mutex for aUsed and aFree */;
+    void const * locker   /* perDb mutex is held on this object's behalf */;
   } perDb;
   struct {
     unsigned nphCacheHits;
@@ -521,21 +525,50 @@ static struct {
 #if S3JNI_ENABLE_AUTOEXT
   struct {
     S3JniAutoExtension *pHead  /* Head of the auto-extension list */;
-    S3JniDb * psOpening  /* handle to the being-opened db. We
-                                  need this so that auto extensions
-                                  can have a consistent view of the
-                                  cross-language db connection and
-                                  behave property if they call further
-                                  db APIs. */;
-    int isRunning              /* True while auto extensions are
-                                  running.  This is used to prohibit
-                                  manipulation of the auto-extension
-                                  list while extensions are
-                                  running. */;
+    S3JniDb * psOpening  /* FIXME: move into envCache. Handle to the
+                            being-opened db. We need this so that auto
+                            extensions can have a consistent view of
+                            the cross-language db connection and
+                            behave property if they call further db
+                            APIs. */;
+    int isRunning        /* True while auto extensions are
+                            running.  This is used to prohibit
+                            manipulation of the auto-extension
+                            list while extensions are
+                            running. */;
   } autoExt;
 #endif
 } S3JniGlobal;
 
+#define MUTEX_ASSERT_LOCKER_ENV                                      \
+  assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
+#define MUTEX_ASSERT_LOCKER_PDB                                      \
+  assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
+#define MUTEX_ASSERT_NOTLOCKER_ENV                                   \
+  assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
+#define MUTEX_ASSERT_NOTLOCKER_PDB                                   \
+  assert( 0 == S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
+#define MUTEX_ENTER_ENV                          \
+  /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \
+  MUTEX_ASSERT_NOTLOCKER_ENV;                      \
+  sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \
+  S3JniGlobal.envCache.locker = env
+#define MUTEX_LEAVE_ENV                          \
+  /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \
+  MUTEX_ASSERT_LOCKER_ENV;                       \
+  S3JniGlobal.envCache.locker = 0;                \
+  sqlite3_mutex_leave( S3JniGlobal.envCache.mutex )
+#define MUTEX_ENTER_PDB                            \
+  /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \
+  MUTEX_ASSERT_NOTLOCKER_PDB;                      \
+  sqlite3_mutex_enter( S3JniGlobal.perDb.mutex );  \
+  S3JniGlobal.perDb.locker = env;
+#define MUTEX_LEAVE_PDB         \
+  /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/  \
+  MUTEX_ASSERT_LOCKER_PDB;      \
+  S3JniGlobal.perDb.locker = 0; \
+  sqlite3_mutex_leave( S3JniGlobal.perDb.mutex )
+
 #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env)
 static void s3jni_oom(JNIEnv * const env){
   (*env)->FatalError(env, "Out of memory.") /* does not return */;
@@ -559,12 +592,14 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){
    insofar as possible. Calls (*env)->FatalError() if allocation of
    an entry fails. That's hypothetically possible but "shouldn't happen."
 */
-FIXME_THREADING(S3JniEnvCache)
 static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){
-  struct S3JniEnvCache * row = S3JniGlobal.envCache.aHead;
+  struct S3JniEnvCache * row;
+  MUTEX_ENTER_ENV;
+  row = S3JniGlobal.envCache.aHead;
   for( ; row; row = row->pNext ){
     if( row->env == env ){
       ++S3JniGlobal.metrics.envCacheHits;
+      MUTEX_LEAVE_ENV;
       return row;
     }
   }
@@ -576,11 +611,7 @@ static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){
     if( row->pNext ) row->pNext->pPrev = 0;
   }else{
     row = sqlite3_malloc(sizeof(S3JniEnvCache));
-    if( !row ){
-      (*env)->FatalError(env, "Maintenance required: S3JniEnvCache is full.")
-        /* Does not return, but cc doesn't know that */;
-      return NULL;
-    }
+    OOM_CHECK(row);
   }
   memset(row, 0, sizeof(*row));
   row->pNext = S3JniGlobal.envCache.aHead;
@@ -616,11 +647,12 @@ static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){
     EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class.");
     fUtf8 = (*env)->GetStaticFieldID(env, klazzSC, "UTF_8",
                                      "Ljava/nio/charset/Charset;");
-    EXCEPTION_IS_FATAL("Error getting StndardCharsets.UTF_8 field.");
+    EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field.");
     row->g.oCharsetUtf8 =
       REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8));
     EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8.");
   }
+  MUTEX_LEAVE_ENV;
   return row;
 }
 
@@ -765,7 +797,6 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p,
     System.out.println(e.getMessage()); // Hi
   }
 */
-FIXME_THREADING(S3JniEnvCache)
 static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){
   S3JniEnvCache * const jc = S3JniGlobal_env_cache(env);
   jmethodID mid;
@@ -865,10 +896,10 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest
 /**
    Clears s's state and moves it to the free-list.
 */
-FIXME_THREADING(perDb)
 static void S3JniDb_set_aside(S3JniDb * const s){
   if(s){
     JNIEnv * const env = s->env;
+    MUTEX_ASSERT_LOCKER_PDB;
     assert(s->pDb && "Else this object is already in the free-list.");
     //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb));
     assert(s->pPrev != s);
@@ -904,39 +935,16 @@ static void S3JniDb_set_aside(S3JniDb * const s){
     //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev));
   }
 }
-
-/**
-   Requires that p has been snipped from any linked list it is
-   in. Clears all Java refs p holds and zeroes out p.
-*/
-static void S3JniEnvCache_clear(S3JniEnvCache * const p){
-  JNIEnv * const env = p->env;
-  if(env){
-    int i;
-    UNREF_G(p->g.cObj);
-    UNREF_G(p->g.cLong);
-    UNREF_G(p->g.cString);
-    UNREF_G(p->g.oCharsetUtf8);
-#ifdef SQLITE_ENABLE_FTS5
-    UNREF_G(p->jFtsExt);
-    UNREF_G(p->jPhraseIter.klazz);
-#endif
-    for( i = 0; i < NphCache_SIZE; ++i ){
-      S3JniNphCache_clear(env, &p->nph[i]);
-    }
-    memset(p, 0, sizeof(S3JniEnvCache));
-  }
-}
-
 /**
    Cleans up all state in S3JniGlobal.perDb for th given JNIEnv.
    Results are undefined if a Java-side db uses the API
    from the given JNIEnv after this call.
 */
-FIXME_THREADING(perDb)
 static void S3JniDb_free_for_env(JNIEnv *env){
-  S3JniDb * ps = S3JniGlobal.perDb.aUsed;
+  S3JniDb * ps;
   S3JniDb * pNext = 0;
+  MUTEX_ENTER_PDB;
+  ps = S3JniGlobal.perDb.aUsed;
   for( ; ps; ps = pNext ){
     pNext = ps->pNext;
     if(ps->env == env){
@@ -946,6 +954,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){
       pNext = pPrev;
     }
   }
+  MUTEX_LEAVE_PDB;
 }
 
 /**
@@ -957,35 +966,43 @@ static void S3JniDb_free_for_env(JNIEnv *env){
    what would otherwise be stale references.
 */
 static int S3JniGlobal_env_uncache(JNIEnv * const env){
-  struct S3JniEnvCache * row = S3JniGlobal.envCache.aHead;
+  struct S3JniEnvCache * row;
+  int i;
+  assert( 0!=S3JniGlobal.envCache.mutex && "Env mutex misuse.");
+  row = S3JniGlobal.envCache.aHead;
   for( ; row; row = row->pNext ){
     if( row->env == env ){
       break;
     }
   }
-  if( !row ) return 0;
+  if( !row ){
+      return 0;
+  }
   if( row->pNext ) row->pNext->pPrev = row->pPrev;
   if( row->pPrev ) row->pPrev->pNext = row->pNext;
   if( S3JniGlobal.envCache.aHead == row ){
     assert( !row->pPrev );
     S3JniGlobal.envCache.aHead = row->pNext;
   }
-  S3JniEnvCache_clear(row);
-  assert( !row->pNext );
-  assert( !row->pPrev );
+  S3JniDb_free_for_env(env);
+  UNREF_G(row->g.cObj);
+  UNREF_G(row->g.cLong);
+  UNREF_G(row->g.cString);
+  UNREF_G(row->g.oCharsetUtf8);
+#ifdef SQLITE_ENABLE_FTS5
+  UNREF_G(row->jFtsExt);
+  UNREF_G(row->jPhraseIter.klazz);
+#endif
+  for( i = 0; i < NphCache_SIZE; ++i ){
+    S3JniNphCache_clear(env, &row->nph[i]);
+  }
+  memset(row, 0, sizeof(S3JniEnvCache));
   row->pNext = S3JniGlobal.envCache.aFree;
   if( row->pNext ) row->pNext->pPrev = row;
   S3JniGlobal.envCache.aFree = row;
-  S3JniDb_free_for_env(env);
   return 1;
 }
 
-static void S3JniGlobal_S3JniEnvCache_clear(void){
-  while( S3JniGlobal.envCache.aHead ){
-    S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env );
-  }
-}
-
 /**
    Searches the NativePointerHolder cache for the given combination.
    If it finds one, it returns it as-is. If it doesn't AND the cache
@@ -1128,14 +1145,15 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zC
 }
 
 /**
-   Extracts the new S3JniDb instance from the free-list, or
-   allocates one if needed, associats it with pDb, and returns.
-   Returns NULL on OOM. pDb MUST be associated with jDb via
-   NativePointerHolder_set().
+   Extracts the new S3JniDb instance from the free-list, or allocates
+   one if needed, associats it with pDb, and returns.  Returns NULL on
+   OOM. pDb MUST, on success of the calling operation, subsequently be
+   associated with jDb via NativePointerHolder_set().
 */
 static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb,
                                jobject jDb){
   S3JniDb * rv;
+  MUTEX_ASSERT_LOCKER_PDB;
   if(S3JniGlobal.perDb.aFree){
     rv = S3JniGlobal.perDb.aFree;
     //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb));
@@ -1185,62 +1203,37 @@ static void S3JniDb_dump(S3JniDb *s){
 #endif
 
 /**
-   Returns the S3JniDb 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.
-
-   The 3rd and 4th args should normally only be non-0 for
-   sqlite3_open(_v2)(). For most other cases, they must be 0, in which
-   case the db handle will be fished out of the jDb object and NULL is
-   returned if jDb does not have any associated S3JniDb.
-
-   If called with a NULL jDb and non-NULL pDb then allocIfNeeded MUST
-   be false and it will look for a matching db object. That usage is
-   required for functions, like sqlite3_context_db_handle(), which
-   return a (sqlite3*) but do not take one as an argument.
+   Returns the S3JniDb object for the given db.
+
+   The 3rd argument should normally only be non-0 for routines which
+   are called from the C library and pass a native db handle instead of
+   a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3
+   object, from which the db is fished out.
+
+   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.
 */
 FIXME_THREADING(S3JniEnvCache)
 FIXME_THREADING(perDb)
-static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb,
-                                sqlite3 *pDb, int allocIfNeeded){
-  S3JniDb * s = S3JniGlobal.perDb.aUsed;
-  if(!jDb){
-    if(pDb){
-      assert(!allocIfNeeded);
-    }else{
-      return 0;
+static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){
+  S3JniDb * s = 0;
+  if(jDb || pDb){
+    MUTEX_ENTER_PDB;
+    s = S3JniGlobal.perDb.aUsed;
+    if(!pDb){
+      assert( jDb );
+      pDb = PtrGet_sqlite3(jDb);
     }
-  }
-  assert(allocIfNeeded ? !!pDb : 1);
-  if(!allocIfNeeded && !pDb){
-    pDb = PtrGet_sqlite3(jDb);
-  }
-  for( ; pDb && s; s = s->pNext){
-    if(s->pDb == pDb) return s;
-  }
-  if(allocIfNeeded){
-    s = S3JniDb_alloc(env, pDb, jDb);
+    for( ; pDb && s; s = s->pNext){
+      if(s->pDb == pDb){
+        break;
+      }
+    }
+    MUTEX_LEAVE_PDB;
   }
   return s;
 }
 
-#if 0
-/**
-   An alternative form which searches for the S3JniDb instance for
-   pDb with no JNIEnv-specific info. This can be (but _should_ it be?)
-   called from the context of a separate JNIEnv than the one mapped
-   to in the returned object. Returns 0 if no match is found.
-*/
-FIXME_THREADING(perDb)
-static S3JniDb * S3JniDb_for_db2(sqlite3 *pDb){
-  S3JniDb * s = S3JniGlobal.perDb.aUsed;
-  for( ; pDb && s; s = s->pNext){
-    if(s->pDb == pDb) return s;
-  }
-  return 0;
-}
-#endif
-
 #if S3JNI_ENABLE_AUTOEXT
 /**
    Unlink ax from S3JniGlobal.autoExt and free it.
@@ -2048,7 +2041,7 @@ static int s3jni_busy_handler(void* pState, int n){
 
 FIXME_THREADING(S3JniEnvCache)
 JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   int rc = 0;
   if(!ps) return (jint)SQLITE_NOMEM;
   if(jBusy){
@@ -2079,7 +2072,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){
 FIXME_THREADING(S3JniEnvCache)
 FIXME_THREADING(perDb)
 JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   if( ps ){
     S3JniHook_unref(env, &ps->busyHandler, 1);
     return sqlite3_busy_timeout(ps->pDb, (int)ms);
@@ -2110,12 +2103,13 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
   int rc = 0;
   S3JniDb * ps = 0;
   assert(version == 1 || version == 2);
-  ps = S3JniDb_for_db(env, jDb, 0, 0);
+  ps = S3JniDb_for_db(env, jDb, 0);
   if(ps){
-    //MARKER(("close()ing db@%p\n", ps->pDb));
     rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb);
+    MUTEX_ENTER_PDB;
     S3JniDb_set_aside(ps)
       /* MUST come after close() because of ps->trace. */;
+    MUTEX_LEAVE_PDB;
     NativePointerHolder_set(env, jDb, 0, S3JniClassNames.sqlite3);
   }
   return (jint)rc;
@@ -2170,7 +2164,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
 FIXME_THREADING(S3JniEnvCache)
 FIXME_THREADING(perDb)
 JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   jobject pOld = 0;
   jmethodID xCallback;
@@ -2288,7 +2282,7 @@ static void s3jni_rollback_hook_impl(void *pP){
 FIXME_THREADING(perDb)
 static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,jobject jDb,
                                           jobject jHook){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   jobject pOld = 0;
   jmethodID xCallback;
@@ -2357,14 +2351,14 @@ JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){
 FIXME_THREADING(perDb)
 JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){
   sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx));
-  S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb, 0) : 0;
+  S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0;
   return ps ? ps->jDb : 0;
 }
 
 JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb,
                                jstring name, jint eTextRep,
                                jobject oCollation){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   int rc;
   const char *zName;
@@ -2457,7 +2451,7 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName,
 JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)(
   JENV_CSELF, jobject jDb, jint op, jstring jStr
 ){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   int rc;
   char *zStr;
 
@@ -2489,7 +2483,7 @@ FIXME_THREADING(perDb)
 JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)(
   JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut
 ){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   int rc;
   switch( ps ? op : 0 ){
     case SQLITE_DBCONFIG_ENABLE_FKEY:
@@ -2538,7 +2532,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer
 }
 
 JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   S3JniEnvCache * const jc = S3JniGlobal_env_cache(env);
   char *zDbName;
   jstring jRv = 0;
@@ -2657,7 +2651,9 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc,
     *zDbName = 0;
     return SQLITE_NOMEM;
   }
+  MUTEX_ENTER_PDB;
   *ps = S3JniDb_alloc(env, 0, *jDb);
+  MUTEX_LEAVE_PDB;
 #if S3JNI_ENABLE_AUTOEXT
   if(*ps){
     assert(!S3JniGlobal.autoExt.psOpening);
@@ -2688,15 +2684,12 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps,
 #endif
   if(*ppDb){
     assert(ps->jDb);
-#if S3JNI_ENABLE_AUTOEXT
-    //MARKER(("*autoExt.pHead=%p, ppDb=%p, ps->pDb=%p\n", S3JniGlobal.autoExt.pHead, *ppDb, ps->pDb));
-    // invalid when an autoext triggers another open():
-    // assert( S3JniGlobal.autoExt.pHead ? *ppDb==ps->pDb : 0==ps->pDb );
-#endif
     ps->pDb = *ppDb;
     NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3);
   }else{
+    MUTEX_ENTER_PDB;
     S3JniDb_set_aside(ps);
+    MUTEX_LEAVE_PDB;
     ps = 0;
   }
   OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0);
@@ -2845,7 +2838,7 @@ static int s3jni_progress_handler_impl(void *pP){
 }
 
 JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress){
-  S3JniDb * ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   jmethodID xCallback;
   if( n<1 || !jProgress ){
@@ -3101,7 +3094,7 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
 }
 
 JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   S3JniHook * const pHook = ps ? &ps->authHook : 0;
 
   if( !ps ) return SQLITE_MISUSE;
@@ -3195,7 +3188,11 @@ JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){
 }
 
 JDECL(jint,1shutdown)(JENV_CSELF){
-  S3JniGlobal_S3JniEnvCache_clear();
+  MUTEX_ENTER_ENV;
+  while( S3JniGlobal.envCache.aHead ){
+    S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env );
+  }
+  MUTEX_LEAVE_ENV;
   /* Do not clear S3JniGlobal.jvm: it's legal to call
      sqlite3_initialize() again to restart the lib. */
   return sqlite3_shutdown();
@@ -3279,7 +3276,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
 }
 
 JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   if( !traceMask || !jTracer ){
     if(ps){
@@ -3330,7 +3327,7 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb,
 
 
 JDECL(jobject,1update_1hook)(JENV_CSELF, jobject jDb, jobject jHook){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jclass klazz;
   jobject pOld = 0;
   jmethodID xCallback;
@@ -3635,7 +3632,7 @@ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){
 }
 
 JDECLFtsApi(jobject,getInstanceForDb)(JENV_CSELF,jobject jDb){
-  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0);
+  S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
   jobject rv = 0;
   if(!ps) return 0;
   else if(ps->jFtsApi){
@@ -4338,7 +4335,11 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){
 */
 JNIEXPORT jboolean JNICALL
 Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){
-  return S3JniGlobal_env_uncache(env) ? JNI_TRUE : JNI_FALSE;
+  int rc;
+  MUTEX_ENTER_ENV;
+  rc = S3JniGlobal_env_uncache(env);
+  MUTEX_LEAVE_ENV;
+  return rc ? JNI_TRUE : JNI_FALSE;
 }
 
 /**
@@ -4402,6 +4403,10 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){
     (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible.");
     return;
   }
+  S3JniGlobal.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+  OOM_CHECK( S3JniGlobal.envCache.mutex );
+  S3JniGlobal.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+  OOM_CHECK( S3JniGlobal.perDb.mutex );
 #if 0
   /* Just for sanity checking... */
   (void)S3JniGlobal_env_cache(env);
index 6722085fddfa8c8308431f715847dea7854d98de..5d741859f19e5019ca6f83d8202cfa9141864eb5 100644 (file)
@@ -1443,6 +1443,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text
 JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64
   (JNIEnv *, jclass, jobject, jbyteArray, jlong, jint);
 
+/*
+ * Class:     org_sqlite_jni_SQLite3Jni
+ * Method:    sqlite3_shutdown
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown
+  (JNIEnv *, jclass);
+
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_status
@@ -1731,14 +1739,6 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1frombind
 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1subtype
   (JNIEnv *, jclass, jobject);
 
-/*
- * Class:     org_sqlite_jni_SQLite3Jni
- * Method:    sqlite3_shutdown
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown
-  (JNIEnv *, jclass);
-
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_do_something_for_developer
index adad7181453b61bfe2de22833c7efd38fb0f0dae..e7aa10ebae7416d3c3d5107916983b9b10b33036 100644 (file)
@@ -195,7 +195,7 @@ public final class SQLite3Jni {
      Achtung: it is as yet unknown whether auto extensions registered
      from one JNIEnv (thread) can be safely called from another.
   */
-  public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback);
+  public static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
 
   public static int sqlite3_bind_blob(
     @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
@@ -205,27 +205,27 @@ public final class SQLite3Jni {
       : sqlite3_bind_blob(stmt, ndx, data, data.length);
   }
 
-  private static synchronized native int sqlite3_bind_blob(
+  private static native int sqlite3_bind_blob(
     @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n
   );
 
-  public static synchronized native int sqlite3_bind_double(
+  public static native int sqlite3_bind_double(
     @NotNull sqlite3_stmt stmt, int ndx, double v
   );
 
-  public static synchronized native int sqlite3_bind_int(
+  public static native int sqlite3_bind_int(
     @NotNull sqlite3_stmt stmt, int ndx, int v
   );
 
-  public static synchronized native int sqlite3_bind_int64(
+  public static native int sqlite3_bind_int64(
     @NotNull sqlite3_stmt stmt, int ndx, long v
   );
 
-  public static synchronized native int sqlite3_bind_null(
+  public static native int sqlite3_bind_null(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native int sqlite3_bind_parameter_count(
+  public static native int sqlite3_bind_parameter_count(
     @NotNull sqlite3_stmt stmt
   );
 
@@ -233,7 +233,7 @@ public final class SQLite3Jni {
   /** A level of indirection required to ensure that the input to the
       C-level function of the same name is a NUL-terminated UTF-8
       string. */
-  private static synchronized native int sqlite3_bind_parameter_index(
+  private static native int sqlite3_bind_parameter_index(
     @NotNull sqlite3_stmt stmt, byte[] paramName
   );
 
@@ -265,15 +265,15 @@ public final class SQLite3Jni {
      SQLITE_TRANSIENT for the final parameter and (B) behaves like
      sqlite3_bind_null() if the data argument is null.
   */
-  private static synchronized native int sqlite3_bind_text(
+  private static native int sqlite3_bind_text(
     @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
   );
 
-  public static synchronized native int sqlite3_bind_zeroblob(
+  public static native int sqlite3_bind_zeroblob(
     @NotNull sqlite3_stmt stmt, int ndx, int n
   );
 
-  public static synchronized native int sqlite3_bind_zeroblob64(
+  public static native int sqlite3_bind_zeroblob64(
     @NotNull sqlite3_stmt stmt, int ndx, long n
   );
 
@@ -283,11 +283,11 @@ public final class SQLite3Jni {
      to clear the busy handler. Calling this multiple times with the
      same object is a no-op on the second and subsequent calls.
   */
-  public static synchronized native int sqlite3_busy_handler(
+  public static native int sqlite3_busy_handler(
     @NotNull sqlite3 db, @Nullable BusyHandler handler
   );
 
-  public static synchronized native int sqlite3_busy_timeout(
+  public static native int sqlite3_busy_timeout(
     @NotNull sqlite3 db, int ms
   );
 
@@ -296,63 +296,63 @@ public final class SQLite3Jni {
      effects, if auto extensions are currently running. (The JNI-level
      list of extensions cannot be manipulated while it is being traversed.)
   */
-  public static synchronized native boolean sqlite3_cancel_auto_extension(
+  public static native boolean sqlite3_cancel_auto_extension(
     @NotNull AutoExtension ax
   );
 
-  public static synchronized native int sqlite3_changes(
+  public static native int sqlite3_changes(
     @NotNull sqlite3 db
   );
 
-  public static synchronized native long sqlite3_changes64(
+  public static native long sqlite3_changes64(
     @NotNull sqlite3 db
   );
 
-  public static synchronized native int sqlite3_clear_bindings(
+  public static native int sqlite3_clear_bindings(
     @NotNull sqlite3_stmt stmt
   );
 
-  public static synchronized native int sqlite3_close(
+  public static native int sqlite3_close(
     @NotNull sqlite3 db
   );
 
-  public static synchronized native int sqlite3_close_v2(
+  public static native int sqlite3_close_v2(
     @NotNull sqlite3 db
   );
 
-  public static synchronized native byte[] sqlite3_column_blob(
+  public static native byte[] sqlite3_column_blob(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native int sqlite3_column_bytes(
+  public static native int sqlite3_column_bytes(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native int sqlite3_column_bytes16(
+  public static native int sqlite3_column_bytes16(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native int sqlite3_column_count(
+  public static native int sqlite3_column_count(
     @NotNull sqlite3_stmt stmt
   );
 
-  public static synchronized native double sqlite3_column_double(
+  public static native double sqlite3_column_double(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native int sqlite3_column_int(
+  public static native int sqlite3_column_int(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native long sqlite3_column_int64(
+  public static native long sqlite3_column_int64(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native String sqlite3_column_name(
+  public static native String sqlite3_column_name(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native String sqlite3_column_database_name(
+  public static native String sqlite3_column_database_name(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
@@ -385,11 +385,11 @@ public final class SQLite3Jni {
     return type.isInstance(o) ? (T)o : null;
   }
 
-  public static synchronized native String sqlite3_column_origin_name(
+  public static native String sqlite3_column_origin_name(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native String sqlite3_column_table_name(
+  public static native String sqlite3_column_table_name(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
@@ -398,7 +398,7 @@ public final class SQLite3Jni {
      This API includes no functions for working with Java's Modified
      UTF-8.
   */
-  public static synchronized native String sqlite3_column_text16(
+  public static native String sqlite3_column_text16(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
@@ -406,7 +406,7 @@ public final class SQLite3Jni {
      Returns the given column's contents as UTF-8-encoded (not MUTF-8) text.
      Use sqlite3_column_text16() to fetch the text
   */
-  public static synchronized native byte[] sqlite3_column_text(
+  public static native byte[] sqlite3_column_text(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
@@ -447,11 +447,11 @@ public final class SQLite3Jni {
   //   return rv;
   // }
 
-  public static synchronized native int sqlite3_column_type(
+  public static native int sqlite3_column_type(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
-  public static synchronized native sqlite3_value sqlite3_column_value(
+  public static native sqlite3_value sqlite3_column_value(
     @NotNull sqlite3_stmt stmt, int ndx
   );
 
@@ -459,7 +459,7 @@ public final class SQLite3Jni {
      This functions like C's sqlite3_collation_needed16() because
      Java's string type is compatible with that interface.
   */
-  public static synchronized native int sqlite3_collation_needed(
+  public static native int sqlite3_collation_needed(
     @NotNull sqlite3 db, @Nullable CollationNeeded callback
   );
 
@@ -467,11 +467,11 @@ public final class SQLite3Jni {
      Returns the db handle passed to sqlite3_open() or
      sqlite3_open_v2(), as opposed to a new wrapper object.
   */
-  public static synchronized native sqlite3 sqlite3_context_db_handle(
+  public static native sqlite3 sqlite3_context_db_handle(
     @NotNull sqlite3_context cx
   );
 
-  public static synchronized native CommitHook sqlite3_commit_hook(
+  public static native CommitHook sqlite3_commit_hook(
     @NotNull sqlite3 db, @Nullable CommitHook hook
   );
 
@@ -483,7 +483,7 @@ public final class SQLite3Jni {
     @NotNull String optName
   );
 
-  public static synchronized native int sqlite3_create_collation(
+  public static native int sqlite3_create_collation(
     @NotNull sqlite3 db, @NotNull String name, int eTextRep,
     @NotNull Collation col
   );
@@ -496,16 +496,16 @@ public final class SQLite3Jni {
      SQLFunction's inner classes (Scalar, Aggregate<T>, and Window<T>)
      for details.
    */
-  public static synchronized native int sqlite3_create_function(
+  public static native int sqlite3_create_function(
     @NotNull sqlite3 db, @NotNull String functionName,
     int nArg, int eTextRep, @NotNull SQLFunction func
   );
 
-  public static synchronized native int sqlite3_data_count(
+  public static native int sqlite3_data_count(
     @NotNull sqlite3_stmt stmt
   );
 
-  public static synchronized native String sqlite3_db_filename(
+  public static native String sqlite3_db_filename(
     @NotNull sqlite3 db, @NotNull String dbName
   );
 
@@ -514,7 +514,7 @@ public final class SQLite3Jni {
      variadic arguments. Returns SQLITE_MISUSE if op is not one of the
      SQLITE_DBCONFIG_... options which uses this call form.
   */
-  public static synchronized native int sqlite3_db_config(
+  public static native int sqlite3_db_config(
     @NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out
   );
 
@@ -525,37 +525,37 @@ public final class SQLite3Jni {
      SQLITE_DBCONFIG_MAINDBNAME, but that set of options may be
      extended in future versions.
   */
-  public static synchronized native int sqlite3_db_config(
+  public static native int sqlite3_db_config(
     @NotNull sqlite3 db, int op, @NotNull String val
   );
 
-  public static synchronized native int sqlite3_db_status(
+  public static native int sqlite3_db_status(
     @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent,
     @NotNull OutputPointer.Int32 pHighwater, boolean reset
   );
 
-  public static synchronized native int sqlite3_errcode(@NotNull sqlite3 db);
+  public static native int sqlite3_errcode(@NotNull sqlite3 db);
 
-  public static synchronized native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt);
+  public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt);
 
-  public static synchronized native int sqlite3_extended_errcode(@NotNull sqlite3 db);
+  public static native int sqlite3_extended_errcode(@NotNull sqlite3 db);
 
-  public static synchronized native boolean sqlite3_extended_result_codes(
+  public static native boolean sqlite3_extended_result_codes(
     @NotNull sqlite3 db, boolean onoff
   );
 
-  public static synchronized native String sqlite3_errmsg(@NotNull sqlite3 db);
+  public static native String sqlite3_errmsg(@NotNull sqlite3 db);
 
-  public static synchronized native String sqlite3_errstr(int resultCode);
+  public static native String sqlite3_errstr(int resultCode);
 
   /**
      Note that the offset values assume UTF-8-encoded SQL.
   */
-  public static synchronized native int sqlite3_error_offset(@NotNull sqlite3 db);
+  public static native int sqlite3_error_offset(@NotNull sqlite3 db);
 
-  public static synchronized native int sqlite3_finalize(@NotNull sqlite3_stmt stmt);
+  public static native int sqlite3_finalize(@NotNull sqlite3_stmt stmt);
 
-  public static synchronized native int sqlite3_initialize();
+  public static native int sqlite3_initialize();
 
   /**
      Design note/FIXME: we have a problem vis-a-vis 'synchronized'
@@ -576,11 +576,11 @@ public final class SQLite3Jni {
   //! See sqlite3_interrupt() for threading concerns.
   public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db);
 
-  public static synchronized native long sqlite3_last_insert_rowid(@NotNull sqlite3 db);
+  public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db);
 
-  public static synchronized native String sqlite3_libversion();
+  public static native String sqlite3_libversion();
 
-  public static synchronized native int sqlite3_libversion_number();
+  public static native int sqlite3_libversion_number();
 
   /**
      Works like its C counterpart and makes the native pointer of the
@@ -601,11 +601,11 @@ public final class SQLite3Jni {
      or sqlite3_open_v2() so that they have a predictible object to
      pass to, e.g., the sqlite3_collation_needed() callback.
   */
-  public static synchronized native int sqlite3_open(
+  public static native int sqlite3_open(
     @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb
   );
 
-  public static synchronized native int sqlite3_open_v2(
+  public static native int sqlite3_open_v2(
     @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb,
     int flags, @Nullable String zVfs
   );
@@ -629,7 +629,7 @@ public final class SQLite3Jni {
      necessary, however, and overloads are provided which gloss over
      that.
   */
-  private static synchronized native int sqlite3_prepare(
+  private static native int sqlite3_prepare(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
     @NotNull OutputPointer.sqlite3_stmt outStmt,
     @Nullable OutputPointer.Int32 pTailOffset
@@ -658,7 +658,7 @@ public final class SQLite3Jni {
     return sqlite3_prepare(db, utf8, utf8.length, outStmt, null);
   }
 
-  private static synchronized native int sqlite3_prepare_v2(
+  private static native int sqlite3_prepare_v2(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
     @NotNull OutputPointer.sqlite3_stmt outStmt,
     @Nullable OutputPointer.Int32 pTailOffset
@@ -687,7 +687,7 @@ public final class SQLite3Jni {
     return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null);
   }
 
-  private static synchronized native int sqlite3_prepare_v3(
+  private static native int sqlite3_prepare_v3(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
     int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt,
     @Nullable OutputPointer.Int32 pTailOffset
@@ -716,22 +716,22 @@ public final class SQLite3Jni {
     return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null);
   }
 
-  public static synchronized native void sqlite3_progress_handler(
+  public static native void sqlite3_progress_handler(
     @NotNull sqlite3 db, int n, @Nullable ProgressHandler h
   );
 
   //TODO??? void *sqlite3_preupdate_hook(...) and friends
 
-  public static synchronized native int sqlite3_reset(@NotNull sqlite3_stmt stmt);
+  public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt);
 
   /**
      Works like the C API except that it has no side effects if auto
      extensions are currently running. (The JNI-level list of
      extensions cannot be manipulated while it is being traversed.)
   */
-  public static synchronized native void sqlite3_reset_auto_extension();
+  public static native void sqlite3_reset_auto_extension();
 
-  public static synchronized native void sqlite3_result_double(
+  public static native void sqlite3_result_double(
     @NotNull sqlite3_context cx, double v
   );
 
@@ -742,7 +742,7 @@ public final class SQLite3Jni {
      results in the C-level sqlite3_result_error() being called with
      a complaint about the invalid argument.
   */
-  private static synchronized native void sqlite3_result_error(
+  private static native void sqlite3_result_error(
     @NotNull sqlite3_context cx, @Nullable byte[] msg,
     int eTextRep
   );
@@ -785,27 +785,27 @@ public final class SQLite3Jni {
     sqlite3_result_error16(cx, e.getMessage());
   }
 
-  public static synchronized native void sqlite3_result_error_toobig(
+  public static native void sqlite3_result_error_toobig(
     @NotNull sqlite3_context cx
   );
 
-  public static synchronized native void sqlite3_result_error_nomem(
+  public static native void sqlite3_result_error_nomem(
     @NotNull sqlite3_context cx
   );
 
-  public static synchronized native void sqlite3_result_error_code(
+  public static native void sqlite3_result_error_code(
     @NotNull sqlite3_context cx, int c
   );
 
-  public static synchronized native void sqlite3_result_null(
+  public static native void sqlite3_result_null(
     @NotNull sqlite3_context cx
   );
 
-  public static synchronized native void sqlite3_result_int(
+  public static native void sqlite3_result_int(
     @NotNull sqlite3_context cx, int v
   );
 
-  public static synchronized native void sqlite3_result_int64(
+  public static native void sqlite3_result_int64(
     @NotNull sqlite3_context cx, long v
   );
 
@@ -825,7 +825,7 @@ public final class SQLite3Jni {
 
      Note that there is no sqlite3_bind_java_object() counterpart.
   */
-  public static synchronized native void sqlite3_result_java_object(
+  public static native void sqlite3_result_java_object(
     @NotNull sqlite3_context cx, @NotNull Object o
   );
 
@@ -883,19 +883,19 @@ public final class SQLite3Jni {
     sqlite3_result_text(cx, v);
   }
 
-  public static synchronized native void sqlite3_result_value(
+  public static native void sqlite3_result_value(
     @NotNull sqlite3_context cx, @NotNull sqlite3_value v
   );
 
-  public static synchronized native void sqlite3_result_zeroblob(
+  public static native void sqlite3_result_zeroblob(
     @NotNull sqlite3_context cx, int n
   );
 
-  public static synchronized native int sqlite3_result_zeroblob64(
+  public static native int sqlite3_result_zeroblob64(
     @NotNull sqlite3_context cx, long n
   );
 
-  private static synchronized native void sqlite3_result_blob(
+  private static native void sqlite3_result_blob(
     @NotNull sqlite3_context cx, @Nullable byte[] blob, int maxLen
   );
 
@@ -915,7 +915,7 @@ public final class SQLite3Jni {
      If maxLen is larger than blob.length, it is truncated to that
      value. If it is negative, results are undefined.
   */
-  private static synchronized native void sqlite3_result_blob64(
+  private static native void sqlite3_result_blob64(
     @NotNull sqlite3_context cx, @Nullable byte[] blob, long maxLen
   );
 
@@ -925,7 +925,7 @@ public final class SQLite3Jni {
     sqlite3_result_blob64(cx, blob, (long)(null==blob ? 0 : blob.length));
   }
 
-  private static synchronized native void sqlite3_result_text(
+  private static native void sqlite3_result_text(
     @NotNull sqlite3_context cx, @Nullable byte[] text, int maxLen
   );
 
@@ -959,17 +959,23 @@ public final class SQLite3Jni {
      text.length, it is silently truncated to text.length. If it is
      negative, results are undefined.
   */
-  private static synchronized native void sqlite3_result_text64(
+  private static native void sqlite3_result_text64(
     @NotNull sqlite3_context cx, @Nullable byte[] text,
     long maxLength, int encoding
   );
 
-  public static synchronized native int sqlite3_status(
+  /**
+     Cleans up all per-JNIEnv and per-db state managed by the library
+     then calls the C-native sqlite3_shutdown().
+  */
+  public static synchronized native int sqlite3_shutdown();
+
+  public static native int sqlite3_status(
     int op, @NotNull OutputPointer.Int32 pCurrent,
     @NotNull OutputPointer.Int32 pHighwater, boolean reset
   );
 
-  public static synchronized native int sqlite3_status64(
+  public static native int sqlite3_status64(
     int op, @NotNull OutputPointer.Int64 pCurrent,
     @NotNull OutputPointer.Int64 pHighwater, boolean reset
   );
@@ -1025,32 +1031,32 @@ public final class SQLite3Jni {
     sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16BE);
   }
 
-  public static synchronized native RollbackHook sqlite3_rollback_hook(
+  public static native RollbackHook sqlite3_rollback_hook(
     @NotNull sqlite3 db, @Nullable RollbackHook hook
   );
 
   //! Sets or unsets (if auth is null) the current authorizer.
-  public static synchronized native int sqlite3_set_authorizer(
+  public static native int sqlite3_set_authorizer(
     @NotNull sqlite3 db, @Nullable Authorizer auth
   );
 
-  public static synchronized native void sqlite3_set_last_insert_rowid(
+  public static native void sqlite3_set_last_insert_rowid(
     @NotNull sqlite3 db, long rowid
   );
 
-  public static synchronized native int sqlite3_sleep(int ms);
+  public static native int sqlite3_sleep(int ms);
 
-  public static synchronized native String sqlite3_sourceid();
+  public static native String sqlite3_sourceid();
 
-  public static synchronized native String sqlite3_sql(@NotNull sqlite3_stmt stmt);
+  public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt);
 
-  public static synchronized native int sqlite3_step(@NotNull sqlite3_stmt stmt);
+  public static native int sqlite3_step(@NotNull sqlite3_stmt stmt);
 
   /**
      Internal impl of the public sqlite3_strglob() method. Neither argument
      may be NULL and both _MUST_ be NUL-terminated.
   */
-  private static synchronized native int sqlite3_strglob(
+  private static native int sqlite3_strglob(
     @NotNull byte[] glob, @NotNull byte[] txt
   );
 
@@ -1067,7 +1073,7 @@ public final class SQLite3Jni {
      Internal impl of the public sqlite3_strlike() method. Neither
      argument may be NULL and both _MUST_ be NUL-terminated.
   */
-  private static synchronized native int sqlite3_strlike(
+  private static native int sqlite3_strlike(
     @NotNull byte[] glob, @NotNull byte[] txt, int escChar
   );
 
@@ -1081,11 +1087,11 @@ public final class SQLite3Jni {
     );
   }
 
-  public static synchronized native int sqlite3_threadsafe();
+  public static native int sqlite3_threadsafe();
 
-  public static synchronized native int sqlite3_total_changes(@NotNull sqlite3 db);
+  public static native int sqlite3_total_changes(@NotNull sqlite3 db);
 
-  public static synchronized native long sqlite3_total_changes64(@NotNull sqlite3 db);
+  public static native long sqlite3_total_changes64(@NotNull sqlite3 db);
 
   /**
      Works like C's sqlite3_trace_v2() except that the 3rd argument to that
@@ -1097,33 +1103,33 @@ public final class SQLite3Jni {
      mapping state fails and SQLITE_ERROR if the given callback object
      cannot be processed propertly (i.e. an internal error).
   */
-  public static synchronized native int sqlite3_trace_v2(
+  public static native int sqlite3_trace_v2(
     @NotNull sqlite3 db, int traceMask, @Nullable Tracer tracer
   );
 
-  public static synchronized native UpdateHook sqlite3_update_hook(
+  public static native UpdateHook sqlite3_update_hook(
     sqlite3 db, UpdateHook hook
   );
 
-  public static synchronized native byte[] sqlite3_value_blob(@NotNull sqlite3_value v);
+  public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_bytes(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_bytes(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_bytes16(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_bytes16(@NotNull sqlite3_value v);
 
-  public static synchronized native double sqlite3_value_double(@NotNull sqlite3_value v);
+  public static native double sqlite3_value_double(@NotNull sqlite3_value v);
 
-  public static synchronized native sqlite3_value sqlite3_value_dupe(
+  public static native sqlite3_value sqlite3_value_dupe(
     @NotNull sqlite3_value v
   );
 
-  public static synchronized native int sqlite3_value_encoding(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_encoding(@NotNull sqlite3_value v);
 
-  public static synchronized native void sqlite3_value_free(@Nullable sqlite3_value v);
+  public static native void sqlite3_value_free(@Nullable sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_int(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_int(@NotNull sqlite3_value v);
 
-  public static synchronized native long sqlite3_value_int64(@NotNull sqlite3_value v);
+  public static native long sqlite3_value_int64(@NotNull sqlite3_value v);
 
   /**
      If the given value was set using sqlite3_result_java_value() then
@@ -1132,7 +1138,7 @@ public final class SQLite3Jni {
      It is up to the caller to inspect the object to determine its
      type, and cast it if necessary.
   */
-  public static synchronized native Object sqlite3_value_java_object(
+  public static native Object sqlite3_value_java_object(
     @NotNull sqlite3_value v
   );
 
@@ -1153,41 +1159,35 @@ public final class SQLite3Jni {
      See sqlite3_value_text_utf8() for how to extract text in standard
      UTF-8.
   */
-  public static synchronized native String sqlite3_value_text(@NotNull sqlite3_value v);
+  public static native String sqlite3_value_text(@NotNull sqlite3_value v);
 
   /**
      The sqlite3_value counterpart of sqlite3_column_text_utf8().
   */
-  public static synchronized native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v);
+  public static native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v);
 
-  public static synchronized native byte[] sqlite3_value_text16(@NotNull sqlite3_value v);
+  public static native byte[] sqlite3_value_text16(@NotNull sqlite3_value v);
 
-  public static synchronized native byte[] sqlite3_value_text16le(@NotNull sqlite3_value v);
+  public static native byte[] sqlite3_value_text16le(@NotNull sqlite3_value v);
 
-  public static synchronized native byte[] sqlite3_value_text16be(@NotNull sqlite3_value v);
+  public static native byte[] sqlite3_value_text16be(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_type(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_type(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_numeric_type(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_numeric_type(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_nochange(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_nochange(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_frombind(@NotNull sqlite3_value v);
+  public static native int sqlite3_value_frombind(@NotNull sqlite3_value v);
 
-  public static synchronized native int sqlite3_value_subtype(@NotNull sqlite3_value v);
-
-  /**
-     Cleans up all per-JNIEnv and per-db state managed by the library
-     then calls the C-native sqlite3_shutdown().
-  */
-  public static synchronized native int sqlite3_shutdown();
+  public static native int sqlite3_value_subtype(@NotNull sqlite3_value v);
 
   /**
      This is NOT part of the public API. It exists solely as a place
      to hook in arbitrary C-side code during development and testing
      of this library.
   */
-  public static synchronized native void sqlite3_do_something_for_developer();
+  public static native void sqlite3_do_something_for_developer();
 
   //////////////////////////////////////////////////////////////////////
   // SQLITE_... constants follow...
index 0c792ccae0cb3c995b0c7d6252f84decf8e120a7..d3b949dbf94d6fd03da07aafbd0640eb323f88bd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bind\ssqlite3_interrupt()\sand\ssqlite3_is_interrupted()\sto\sJNI\sbut\swith\scaveats\sregarding\smutexing\sof\sthe\sJNIEnv\scache.\sAdd\sa\sloud\swarning\sto\sthe\sJNI\s'dist'\starget\sthat\sit\sshould\sbe\sbuilt\swith\sJDK8\s(a.k.a.\sJava\s1.8)\sfor\scompatibility\sreasons.
-D 2023-08-12T23:47:58.408
+C An\sinitial\sattempt\sat\sprotecting\sthe\sJNI\sglobal\sstate\svia\smutexes\sat\sthe\sC\slevel\sinstead\sof\srelying\son\sJava's\ssynchronized\skeyword.\sIt\sseems\sto\swork\sbut\sincreases\sthe\srun\stime\sof\sthe\ssingle-threaded\sbatch\stester\sby\sroughly\s3\stimes.
+D 2023-08-13T09:53:27.824
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -234,8 +234,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881
 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb
 F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4
-F ext/jni/src/c/sqlite3-jni.c 1d3bb5113ba4dd7f8645fcc4c669155931e44e234816f528642a738914dd45a4
-F ext/jni/src/c/sqlite3-jni.h 11bf3ab9682f5c393e6ac6a3ddb0fdf7b8dd40f7c77f9ef122d3e5c011a5d329
+F ext/jni/src/c/sqlite3-jni.c 93fbeed06441352df68f6c0e4bc2cd895e81d8550dc9972cafb30621958b4784
+F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18
 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093
 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
@@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495
 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86
 F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
 F ext/jni/src/org/sqlite/jni/SQLFunction.java 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1652af40fc0acb7a140dbe32e3146f980c37c28454b5115a4d0856cbdbc52696
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 7933fabc267dbfdccb784563d8c404f90275fe9418e8cd6c43c584bc9c57f70f
 F ext/jni/src/org/sqlite/jni/Tester1.java fc2ec1f1be58474112b9df8284f0157b64872107f446154c3d0bf1742b924d2b
 F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1
 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
@@ -2091,8 +2091,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 48b13edcec6935bf125b265b41a3e6f7b2407afff89d5b4daa2939e3c5679ca0
-R 122775c5939e6f1540fadefe535658be
+P fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe
+R 42a3d0d4c63d13c26ffa7bd8f71380ed
 U stephan
-Z a13d97b8681eef4145a4ab842cd3a9d2
+Z 5906c49c68c9155fc27b7d9430c5551e
 # Remove this line to create a well-formed Fossil manifest.
index 97ea0d6766a107d738afbf886cfb0572e1d7936d..684bcd68e8542cbc957885069ac0e9ddbe947001 100644 (file)
@@ -1 +1 @@
-fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe
\ No newline at end of file
+c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4
\ No newline at end of file