]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a mutex for auto-extensions, tied in to the open() process since that's the route...
authorstephan <stephan@noemail.net>
Sun, 13 Aug 2023 12:40:27 +0000 (12:40 +0000)
committerstephan <stephan@noemail.net>
Sun, 13 Aug 2023 12:40:27 +0000 (12:40 +0000)
FossilOrigin-Name: 8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1

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

index eebe124f8941d6b2cc78aa355e59c71da2257ce9..149feef554970666bc92d5f1d82013baf30f660d 100644 (file)
@@ -400,8 +400,6 @@ static void S3JniNphCache_clear(JNIEnv * const env, S3JniNphCache * const p){
   memset(p, 0, sizeof(S3JniNphCache));
 }
 
-#define S3JNI_ENABLE_AUTOEXT 1
-#if S3JNI_ENABLE_AUTOEXT
 /*
   Whether auto extensions are feasible here is currently unknown due
   to...
@@ -430,7 +428,6 @@ struct S3JniAutoExtension {
   S3JniAutoExtension *pNext  /* next linked-list entry */;
   S3JniAutoExtension *pPrev  /* previous linked-list entry */;
 };
-#endif
 
 /** State for various hook callbacks. */
 typedef struct S3JniHook S3JniHook;
@@ -518,6 +515,7 @@ static struct {
     unsigned envCacheMisses;
     unsigned nMutexEnv       /* number of times envCache.mutex was entered */;
     unsigned nMutexPerDb     /* number of times perDb.mutex was entered */;
+    unsigned nMutexAutoExt   /* number of times autoExt.mutex was entered */;
     unsigned nDestroy        /* xDestroy() calls across all types */;
     struct {
       /* Number of calls for each type of UDF callback. */
@@ -528,10 +526,9 @@ static struct {
       unsigned nInverse;
     } udf;
   } metrics;
-#if S3JNI_ENABLE_AUTOEXT
   struct {
     S3JniAutoExtension *pHead  /* Head of the auto-extension list */;
-    S3JniDb * psOpening  /* FIXME: move into envCache. Handle to the
+    S3JniDb * pdbOpening /* 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
@@ -542,36 +539,50 @@ static struct {
                             manipulation of the auto-extension
                             list while extensions are
                             running. */;
+    sqlite3_mutex * mutex /* mutex for aUsed and aFree */;
+    void const * locker   /* Mutex is locked on this object's behalf */;
   } autoExt;
-#endif
 } S3JniGlobal;
 
 #define MUTEX_ASSERT_LOCKER_ENV                                      \
   assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
 #define MUTEX_ASSERT_NOTLOCKER_ENV                                   \
   assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
-#define MUTEX_ENTER_ENV                          \
+#define MUTEX_ENTER_ENV                                       \
   /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \
-  MUTEX_ASSERT_NOTLOCKER_ENV;                      \
-  sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \
-  ++S3JniGlobal.metrics.nMutexEnv;                \
+  MUTEX_ASSERT_NOTLOCKER_ENV;                                 \
+  sqlite3_mutex_enter( S3JniGlobal.envCache.mutex );          \
+  ++S3JniGlobal.metrics.nMutexEnv;                            \
   S3JniGlobal.envCache.locker = env
-#define MUTEX_LEAVE_ENV                          \
+#define MUTEX_LEAVE_ENV                                       \
   /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \
-  MUTEX_ASSERT_LOCKER_ENV;                       \
-  S3JniGlobal.envCache.locker = 0;                \
+  MUTEX_ASSERT_LOCKER_ENV;                                    \
+  S3JniGlobal.envCache.locker = 0;                            \
   sqlite3_mutex_leave( S3JniGlobal.envCache.mutex )
-#define MUTEX_ASSERT_LOCKED_PDB                                      \
+#define MUTEX_ASSERT_LOCKED_PDB  \
   assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
-#define MUTEX_ENTER_PDB                            \
+#define MUTEX_ENTER_PDB                                         \
   /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \
-  sqlite3_mutex_enter( S3JniGlobal.perDb.mutex );  \
-  ++S3JniGlobal.metrics.nMutexPerDb;               \
+  sqlite3_mutex_enter( S3JniGlobal.perDb.mutex );               \
+  ++S3JniGlobal.metrics.nMutexPerDb;                            \
   S3JniGlobal.perDb.locker = env;
-#define MUTEX_LEAVE_PDB         \
+#define MUTEX_LEAVE_PDB                                         \
   /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/  \
-  S3JniGlobal.perDb.locker = 0; \
+  S3JniGlobal.perDb.locker = 0;                                 \
   sqlite3_mutex_leave( S3JniGlobal.perDb.mutex )
+#define MUTEX_ENTER_EXT                                           \
+  /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \
+  sqlite3_mutex_enter( S3JniGlobal.autoExt.mutex );               \
+  ++S3JniGlobal.metrics.nMutexAutoExt
+#define MUTEX_TRY_EXT(FAIL_EXPR)                                     \
+  /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/       \
+  if( sqlite3_mutex_try( S3JniGlobal.autoExt.mutex ) ){ FAIL_EXPR; } \
+  S3JniGlobal.autoExt.locker = env;                                  \
+  ++S3JniGlobal.metrics.nMutexAutoExt
+#define MUTEX_LEAVE_EXT                                         \
+  /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/  \
+  S3JniGlobal.autoExt.locker = 0;                               \
+  sqlite3_mutex_leave( S3JniGlobal.autoExt.mutex )
 
 #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env)
 static void s3jni_oom(JNIEnv * const env){
@@ -1023,7 +1034,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){
    This simple cache catches >99% of searches in the current
    (2023-07-31) tests.
 */
-FIXME_THREADING(S3JniEnvCache)
 static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zClassName){
   /**
      According to:
@@ -1041,8 +1051,8 @@ static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zCl
      cached as well.
 
      Reminder: we do not need a mutex for the envRow->nph cache
-     because all nph entries are per-thread and envCache.mutex
-     already guards the fetching of envRow.
+     because all nph entries are per-thread and envCache.mutex already
+     guards the fetching of envRow.
   */
   struct S3JniEnvCache * const envRow = S3JniGlobal_env_cache(env);
   S3JniNphCache * freeSlot = 0;
@@ -1221,8 +1231,6 @@ static void S3JniDb_dump(S3JniDb *s){
    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){
   S3JniDb * s = 0;
   if(jDb || pDb){
@@ -1242,7 +1250,6 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){
   return s;
 }
 
-#if S3JNI_ENABLE_AUTOEXT
 /**
    Unlink ax from S3JniGlobal.autoExt and free it.
 */
@@ -1279,7 +1286,7 @@ static S3JniAutoExtension * S3JniAutoExtension_alloc(JNIEnv *const env,
       return 0;
     }
     ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint",
-                                     "(Lorg/sqlite/jni/sqlite3;)I");
+                                      "(Lorg/sqlite/jni/sqlite3;)I");
     if(!ax->midFunc){
       MARKER(("Error getting xEntryPoint(sqlite3) from object."));
       S3JniAutoExtension_free(env, ax);
@@ -1292,7 +1299,6 @@ static S3JniAutoExtension * S3JniAutoExtension_alloc(JNIEnv *const env,
   }
   return ax;
 }
-#endif /* S3JNI_ENABLE_AUTOEXT */
 
 /**
    Requires that jCx be a Java-side sqlite3_context wrapper for pCx.
@@ -1881,35 +1887,37 @@ WRAP_INT_SVALUE(1value_1numeric_1type, sqlite3_value_numeric_type)
 WRAP_INT_SVALUE(1value_1subtype,       sqlite3_value_subtype)
 WRAP_INT_SVALUE(1value_1type,          sqlite3_value_type)
 
-#if S3JNI_ENABLE_AUTOEXT
+static JNIEnv * s3jni_get_env(void){
+  JNIEnv * env = 0;
+  if( (*S3JniGlobal.jvm)->GetEnv(S3JniGlobal.jvm, (void **)&env,
+                                 JNI_VERSION_1_8) ){
+    fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n");
+    abort();
+  }
+  return env;
+}
+
 /* Central auto-extension handler. */
-FIXME_THREADING(autoExt)
-static int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr,
-                                const struct sqlite3_api_routines *ignored){
+static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
+                                          const struct sqlite3_api_routines *ignored){
   S3JniAutoExtension const * pAX = S3JniGlobal.autoExt.pHead;
   int rc;
   JNIEnv * env = 0;
-  S3JniDb * const ps = S3JniGlobal.autoExt.psOpening;
-  //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb));
-  S3JniGlobal.autoExt.psOpening = 0;
+  S3JniDb * const ps = S3JniGlobal.autoExt.pdbOpening;
+
+  assert( S3JniGlobal.autoExt.locker );
+  assert( S3JniGlobal.autoExt.locker == ps );
+  S3JniGlobal.autoExt.pdbOpening = 0;
   if( !pAX ){
     assert( 0==S3JniGlobal.autoExt.isRunning );
     return 0;
-  }
-  else if( S3JniGlobal.autoExt.isRunning ){
-    /* Necessary to avoid certain endless loop/stack overflow cases. */
-    *pzErr = sqlite3_mprintf("Auto-extensions must not be triggered while "
-                             "auto-extensions are running.");
-    return SQLITE_MISUSE;
-  }
-  else if(!ps){
-    MARKER(("Internal error: cannot find S3JniDb for auto-extension\n"));
-    return SQLITE_ERROR;
-  }else if( (*S3JniGlobal.jvm)->GetEnv(S3JniGlobal.jvm, (void **)&env, JNI_VERSION_1_8) ){
-    assert(!"Cannot get JNIEnv");
-    *pzErr = sqlite3_mprintf("Could not get current JNIEnv.");
+  }else if( S3JniGlobal.autoExt.locker != ps ) {
+    *pzErr = sqlite3_mprintf("Internal error: unexpected path lead to "
+                             "running an auto-extension.");
     return SQLITE_ERROR;
   }
+  env = s3jni_get_env();
+  //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb));
   assert( !ps->pDb /* it's still being opened */ );
   ps->pDb = pDb;
   assert( ps->jDb );
@@ -1941,8 +1949,9 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){
   S3JniAutoExtension * ax;
 
   if( !jAutoExt ) return SQLITE_MISUSE;
-  else if( 0==once && ++once ){
-    sqlite3_auto_extension( (void(*)(void))s3jni_auto_extension );
+  MUTEX_ENTER_EXT;
+  if( 0==once && ++once ){
+    sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions );
   }
   ax = S3JniGlobal.autoExt.pHead;
   for( ; ax; ax = ax->pNext ){
@@ -1950,9 +1959,10 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){
       return 0 /* C API treats this as a no-op. */;
     }
   }
-  return S3JniAutoExtension_alloc(env, jAutoExt) ? 0 : SQLITE_NOMEM;
+  ax = S3JniAutoExtension_alloc(env, jAutoExt);
+  MUTEX_LEAVE_EXT;
+  return ax ? 0 : SQLITE_NOMEM;
 }
-#endif /* S3JNI_ENABLE_AUTOEXT */
 
 FIXME_THREADING(S3JniEnvCache)
 JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt,
@@ -2088,20 +2098,23 @@ JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){
   return SQLITE_MISUSE;
 }
 
-#if S3JNI_ENABLE_AUTOEXT
 FIXME_THREADING(autoExt)
 JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){
-  S3JniAutoExtension * ax;;
-  if( S3JniGlobal.autoExt.isRunning ) return JNI_FALSE;
-  for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){
-    if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){
-      S3JniAutoExtension_free(env, ax);
-      return JNI_TRUE;
+  S3JniAutoExtension * ax;
+  jboolean rc = JNI_FALSE;
+  MUTEX_ENTER_EXT;
+  if( !S3JniGlobal.autoExt.isRunning ) {
+    for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){
+      if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){
+        S3JniAutoExtension_free(env, ax);
+        rc = JNI_TRUE;
+        break;
+      }
     }
   }
-  return JNI_FALSE;
+  MUTEX_LEAVE_EXT;
+  return rc;
 }
-#endif /* S3JNI_ENABLE_AUTOEXT */
 
 
 /**
@@ -2649,27 +2662,41 @@ JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){
 static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc,
                           jstring jDbName, char **zDbName,
                           S3JniDb ** ps, jobject *jDb){
+  int rc = 0;
+  MUTEX_TRY_EXT(return SQLITE_BUSY);
   *jc = S3JniGlobal_env_cache(env);
-  if(!*jc) return SQLITE_NOMEM;
+  if(!*jc){
+    rc = SQLITE_NOMEM;
+    goto end;
+  }
   *zDbName = jDbName ? s3jni_jstring_to_utf8(*jc, jDbName, 0) : 0;
-  if(jDbName && !*zDbName) return SQLITE_NOMEM;
+  if(jDbName && !*zDbName){
+    rc = SQLITE_NOMEM;
+    goto end;
+  }
   *jDb = new_sqlite3_wrapper(env, 0);
   if( !*jDb ){
     sqlite3_free(*zDbName);
     *zDbName = 0;
-    return SQLITE_NOMEM;
+    rc = SQLITE_NOMEM;
+    goto end;
   }
   MUTEX_ENTER_PDB;
   *ps = S3JniDb_alloc(env, 0, *jDb);
   MUTEX_LEAVE_PDB;
-#if S3JNI_ENABLE_AUTOEXT
   if(*ps){
-    assert(!S3JniGlobal.autoExt.psOpening);
-    S3JniGlobal.autoExt.psOpening = *ps;
+    S3JniGlobal.autoExt.pdbOpening = *ps;
+    S3JniGlobal.autoExt.locker = *ps;
+  }else{
+    rc = SQLITE_NOMEM;
   }
-#endif
   //MARKER(("pre-open ps@%p\n", *ps));
-  return *ps ? 0 : SQLITE_NOMEM;
+end:
+  /* Remain in autoExt.mutex until s3jni_open_post(). */
+  if(rc){
+    MUTEX_LEAVE_EXT;
+  }
+  return rc;
 }
 
 /**
@@ -2686,10 +2713,8 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc,
 static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps,
                            sqlite3 **ppDb, jobject jOut, int theRc){
   //MARKER(("post-open() ps@%p db@%p\n", ps, *ppDb));
-#if S3JNI_ENABLE_AUTOEXT
-  assert( S3JniGlobal.autoExt.pHead ? ps!=S3JniGlobal.autoExt.psOpening : 1 );
-  S3JniGlobal.autoExt.psOpening = 0;
-#endif
+  assert( S3JniGlobal.autoExt.locker == ps );
+  S3JniGlobal.autoExt.pdbOpening = 0;
   if(*ppDb){
     assert(ps->jDb);
     ps->pDb = *ppDb;
@@ -2701,6 +2726,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps,
     ps = 0;
   }
   OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0);
+  MUTEX_LEAVE_EXT;
   return theRc;
 }
 
@@ -2710,8 +2736,8 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){
   jobject jDb = 0;
   S3JniDb * ps = 0;
   S3JniEnvCache * jc = 0;
-  S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening;
-  int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb);
+  S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening;
+  int rc= s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb);
   if( 0==rc ){
     rc = sqlite3_open(zName, &pOut);
     //MARKER(("env=%p, *env=%p\n", env, *env));
@@ -2720,7 +2746,7 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){
     assert(rc==0 ? pOut!=0 : 1);
     sqlite3_free(zName);
   }
-  S3JniGlobal.autoExt.psOpening = prevOpening;
+  S3JniGlobal.autoExt.pdbOpening = prevOpening;
   return (jint)rc;
 }
 
@@ -2732,7 +2758,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
   S3JniDb * ps = 0;
   S3JniEnvCache * jc = 0;
   char *zVfs = 0;
-  S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening;
+  S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening;
   int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb);
   if( 0==rc && strVfs ){
     zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0);
@@ -2747,10 +2773,10 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
   /*MARKER(("zName=%s, zVfs=%s, pOut=%p, flags=%d, nrc=%d\n",
     zName, zVfs, pOut, (int)flags, nrc));*/
   rc = s3jni_open_post(env, ps, &pOut, jOut, rc);
+  S3JniGlobal.autoExt.pdbOpening = prevOpening;
   assert(rc==0 ? pOut!=0 : 1);
   sqlite3_free(zName);
   sqlite3_free(zVfs);
-  S3JniGlobal.autoExt.psOpening = prevOpening;
   return (jint)rc;
 }
 
@@ -2886,15 +2912,13 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){
   return rc;
 }
 
-#if S3JNI_ENABLE_AUTOEXT
 JDECL(void,1reset_1auto_1extension)(JENV_CSELF){
-  if(!S3JniGlobal.autoExt.isRunning){
-    while( S3JniGlobal.autoExt.pHead ){
-      S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead);
-    }
+  MUTEX_ENTER_EXT;
+  while( S3JniGlobal.autoExt.pHead ){
+    S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead);
   }
+  MUTEX_LEAVE_EXT;
 }
-#endif /* S3JNI_ENABLE_AUTOEXT */
 
 /* sqlite3_result_text/blob() and friends. */
 static void result_blob_text(int asBlob, int as64,
@@ -3504,9 +3528,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){
   printf("\tJNIEnv cache               %u misses, %u hits\n",
          S3JniGlobal.metrics.envCacheMisses,
          S3JniGlobal.metrics.envCacheHits);
-  printf("\tMutex entry: %u env, %u perDb\n",
+  printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt (mostly via open[_v2]())\n",
          S3JniGlobal.metrics.nMutexEnv,
-         S3JniGlobal.metrics.nMutexPerDb);
+         S3JniGlobal.metrics.nMutexPerDb,
+         S3JniGlobal.metrics.nMutexAutoExt);
   puts("Java-side UDF calls:");
 #define UDF(T) printf("\t%-8s = %u\n", "x" #T, S3JniGlobal.metrics.udf.n##T)
   UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse);
@@ -4418,6 +4443,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){
   OOM_CHECK( S3JniGlobal.envCache.mutex );
   S3JniGlobal.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
   OOM_CHECK( S3JniGlobal.perDb.mutex );
+  S3JniGlobal.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+  OOM_CHECK( S3JniGlobal.autoExt.mutex );
 #if 0
   /* Just for sanity checking... */
   (void)S3JniGlobal_env_cache(env);
index e7aa10ebae7416d3c3d5107916983b9b10b33036..dbd2e4bae7015127f830a0300f5100a7310e5db7 100644 (file)
@@ -178,24 +178,25 @@ public final class SQLite3Jni {
        not have access to the sqlite3_api object which native
        auto-extensions do.
 
-     - If an auto-extension opens a db, thereby triggering recursion
-       in the auto-extension handler, it will fail with a message
-       explaining that recursion is not permitted.
+     - If an auto-extension opens a db, opening will fail with SQLITE_BUSY.
+       The alternative would be endless recursion into the auto-extension.
 
-     - All of the other auto extension routines will fail without side
-       effects if invoked from within the execution of an
-       auto-extension. i.e. auto extensions can neither be added,
+     - The list of auto-extensions must not be manipulated from within
+       an auto-extension. Auto extensions can neither be added,
        removed, nor cleared while one registered with this function is
-       running. Auto-extensions registered directly with the library
-       via C code, as opposed to indirectly via Java, do not have that
-       limitation.
+       running. Attempting to do so may lead to a deadlock.
 
      See the AutoExtension class docs for more information.
 
      Achtung: it is as yet unknown whether auto extensions registered
      from one JNIEnv (thread) can be safely called from another.
+
+     Design note: this family of methods is synchronized in order to
+     help avoid a small race condition where an in-progress
+     sqlite3_reset_auto_extension() or sqlite3_cancel_auto_extension()
+     could cause sqlite3_open() to fail with SQLITE_BUSY.
   */
-  public static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
+  public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback);
 
   public static int sqlite3_bind_blob(
     @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
@@ -296,7 +297,7 @@ 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 native boolean sqlite3_cancel_auto_extension(
+  public static synchronized native boolean sqlite3_cancel_auto_extension(
     @NotNull AutoExtension ax
   );
 
@@ -313,11 +314,11 @@ public final class SQLite3Jni {
   );
 
   public static native int sqlite3_close(
-    @NotNull sqlite3 db
+    @Nullable sqlite3 db
   );
 
   public static native int sqlite3_close_v2(
-    @NotNull sqlite3 db
+    @Nullable sqlite3 db
   );
 
   public static native byte[] sqlite3_column_blob(
@@ -593,19 +594,16 @@ public final class SQLite3Jni {
      Recall that even if opening fails, the output pointer might be
      non-null. Any error message about the failure will be in that
      object and it is up to the caller to sqlite3_close() that
-     db handle.
+     db handle. Passing a null to sqlite3_close() is legal.
 
-     Pedantic note: though any number of Java-level sqlite3 objects
-     may refer to/wrap a single C-level (sqlite3*), the JNI internals
-     take a reference to the object which is passed to sqlite3_open()
-     or sqlite3_open_v2() so that they have a predictible object to
-     pass to, e.g., the sqlite3_collation_needed() callback.
+     Design note: this method is synchronized in order to help
+     alleviate a race condition involving auto-extensions.
   */
-  public static native int sqlite3_open(
+  public static synchronized native int sqlite3_open(
     @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb
   );
 
-  public static native int sqlite3_open_v2(
+  public static synchronized native int sqlite3_open_v2(
     @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb,
     int flags, @Nullable String zVfs
   );
@@ -729,7 +727,7 @@ public final class SQLite3Jni {
      extensions are currently running. (The JNI-level list of
      extensions cannot be manipulated while it is being traversed.)
   */
-  public static native void sqlite3_reset_auto_extension();
+  public static synchronized native void sqlite3_reset_auto_extension();
 
   public static native void sqlite3_result_double(
     @NotNull sqlite3_context cx, double v
index 48db0129dae17c23a164a61c40ed728431f193d9..dbbd37eaaa0cde144d46b6ecb5965686f9fa211c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssome\sdocs\sand\smetrics\sfor\sthe\snew\smutex\sinternals.
-D 2023-08-13T10:28:35.456
+C Add\sa\smutex\sfor\sauto-extensions,\stied\sin\sto\sthe\sopen()\sprocess\ssince\sthat's\sthe\sroute\sinto\sauto-extensions.
+D 2023-08-13T12:40:27.217
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -234,7 +234,7 @@ 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 44c1da6d1ae9d8ea2af16dd5c1d17224a655c9c94135892f81571b2481896431
+F ext/jni/src/c/sqlite3-jni.c 4f6f8f2dec309a6b117a6e8f460078b5f6f6b65a17c530ae2f5907b6425d714c
 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
@@ -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 7933fabc267dbfdccb784563d8c404f90275fe9418e8cd6c43c584bc9c57f70f
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5eeba0b1a00fb34bc93fe60186f6032fcf4d568fc5868d70029883d3d07cc306
 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 c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4
-R cd02d0fb58628c824a2d7de7efc47b02
+P 33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a
+R 6a1ce79c2368c04d74cde070b44b6572
 U stephan
-Z 6ab46f9c79559c9df9412a6d1bef3261
+Z d637a436f204decfbfbf40fa44769c67
 # Remove this line to create a well-formed Fossil manifest.
index 0fe79c981a4f2329a4defbdd11ba0b07ec07af5f..4f1f622ee94a08e994cb8cb363ed8f4af5c90ea5 100644 (file)
@@ -1 +1 @@
-33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a
\ No newline at end of file
+8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1
\ No newline at end of file