mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Disassociate JNI db handles from the thread that created them, as it's no longer relevant.
FossilOrigin-Name: 8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf
This commit is contained in:
@ -380,10 +380,6 @@ struct S3JniHook{
|
||||
*/
|
||||
typedef struct S3JniDb S3JniDb;
|
||||
struct S3JniDb {
|
||||
JNIEnv *env /* Used for cleaning up all dbs owned by a given
|
||||
** thread, noting that this ownership is an artificial
|
||||
** one imposed by our threading constraints, not by
|
||||
** the core library. */;
|
||||
sqlite3 *pDb /* The associated db handle */;
|
||||
jobject jDb /* A global ref of the output object which gets
|
||||
returned from sqlite3_open(_v2)(). We need this in
|
||||
@ -558,6 +554,9 @@ struct S3JniGlobalType {
|
||||
static S3JniGlobalType S3JniGlobal = {};
|
||||
#define SJG S3JniGlobal
|
||||
|
||||
/* Helpers for working with specific mutexes. */
|
||||
#define MUTEX_ENV_ASSERT_LOCKED \
|
||||
assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
|
||||
#define MUTEX_ENV_ASSERT_LOCKER \
|
||||
assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
|
||||
#define MUTEX_ENV_ASSERT_NOTLOCKER \
|
||||
@ -604,7 +603,7 @@ static S3JniGlobalType S3JniGlobal = {};
|
||||
assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
|
||||
|
||||
#define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env)
|
||||
static void s3jni_oom(JNIEnv * const env){
|
||||
static inline void s3jni_oom(JNIEnv * const env){
|
||||
(*env)->FatalError(env, "Out of memory.") /* does not return */;
|
||||
}
|
||||
|
||||
@ -941,41 +940,15 @@ static void S3JniDb_set_aside(S3JniDb * const s){
|
||||
//if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev));
|
||||
}
|
||||
}
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
static void S3JniDb_free_for_env(JNIEnv *env){
|
||||
S3JniDb * ps;
|
||||
S3JniDb * pNext = 0;
|
||||
MUTEX_PDB_ENTER;
|
||||
ps = SJG.perDb.aUsed;
|
||||
for( ; ps; ps = pNext ){
|
||||
pNext = ps->pNext;
|
||||
if(ps->env == env){
|
||||
#ifndef NDEBUG
|
||||
S3JniDb * const pPrev = ps->pPrev;
|
||||
#endif
|
||||
S3JniDb_set_aside(ps);
|
||||
assert( pPrev ? pPrev->pNext==pNext : 1 );
|
||||
assert( ps == SJG.perDb.aFree );
|
||||
}
|
||||
}
|
||||
MUTEX_PDB_LEAVE;
|
||||
}
|
||||
|
||||
/**
|
||||
Uncache any state for the given JNIEnv, clearing all Java
|
||||
references the cache owns. Returns true if env was cached and false
|
||||
if it was not found in the cache.
|
||||
|
||||
Also passes env to S3JniDb_free_for_env() to free up
|
||||
what would otherwise be stale references.
|
||||
*/
|
||||
static int S3JniGlobal_env_uncache(JNIEnv * const env){
|
||||
struct S3JniEnv * row;
|
||||
MUTEX_ENV_ASSERT_LOCKER;
|
||||
MUTEX_ENV_ASSERT_LOCKED;
|
||||
row = SJG.envCache.aHead;
|
||||
for( ; row; row = row->pNext ){
|
||||
if( row->env == env ){
|
||||
@ -992,7 +965,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){
|
||||
assert( !row->pPrev );
|
||||
SJG.envCache.aHead = row->pNext;
|
||||
}
|
||||
S3JniDb_free_for_env(env);
|
||||
memset(row, 0, sizeof(S3JniEnv));
|
||||
row->pNext = SJG.envCache.aFree;
|
||||
if( row->pNext ) row->pNext->pPrev = row;
|
||||
@ -1130,7 +1102,6 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb,
|
||||
}
|
||||
rv->jDb = REF_G(jDb);
|
||||
rv->pDb = pDb;
|
||||
rv->env = env;
|
||||
}
|
||||
MUTEX_PDB_LEAVE;
|
||||
return rv;
|
||||
|
@ -122,21 +122,13 @@ public final class SQLite3Jni {
|
||||
uncacheJniEnv() when it is done with the library - either right
|
||||
before it terminates or when it is finished using the SQLite API.
|
||||
This will clean up any cached per-JNIEnv info. Calling into the
|
||||
library again after that "should" re-initialize the cache on
|
||||
demand, but that's untested.
|
||||
library will re-initialize the cache on demand.
|
||||
|
||||
This call forcibly wipes out all cached information for the
|
||||
current JNIEnv, a side-effect of which is that behavior is
|
||||
undefined if any database objects are (A) still active at the
|
||||
time it is called _and_ (B) calls are subsequently made into the
|
||||
library with such a database. Doing so will, at best, lead to a
|
||||
crash. At worst, it will lead to the db possibly misbehaving
|
||||
because some of its Java-bound state has been cleared. There is
|
||||
no immediate harm in (A) so long as condition (B) is not met.
|
||||
This process does _not_ actually close any databases or finalize
|
||||
any prepared statements. For proper library behavior, and to
|
||||
avoid C-side leaks, be sure to close them before calling this
|
||||
function.
|
||||
This process does _not_ close any databases or finalize
|
||||
any prepared statements because their ownership does not depend on
|
||||
a given thread. For proper library behavior, and to
|
||||
avoid C-side leaks, be sure to finalize all statements and close
|
||||
all databases before calling this function.
|
||||
|
||||
Calling this from the main application thread is not strictly
|
||||
required but is "polite." Additional threads must call this
|
||||
|
@ -24,6 +24,7 @@ import java.util.concurrent.Future;
|
||||
public class Tester1 implements Runnable {
|
||||
//! True when running in multi-threaded mode.
|
||||
private static boolean mtMode = false;
|
||||
private static boolean takeNaps = false;
|
||||
|
||||
private static final class Metrics {
|
||||
int dbOpen;
|
||||
@ -1167,32 +1168,38 @@ public class Tester1 implements Runnable {
|
||||
outln("Woke up.");
|
||||
}
|
||||
|
||||
private void nap() throws InterruptedException {
|
||||
if( takeNaps ){
|
||||
Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 28), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void runTests(boolean fromThread) throws Exception {
|
||||
if(false) testCompileOption();
|
||||
testToUtf8();
|
||||
test1();
|
||||
testOpenDb1();
|
||||
testOpenDb2();
|
||||
testCollation();
|
||||
testPrepare123();
|
||||
testBindFetchInt();
|
||||
testBindFetchInt64();
|
||||
testBindFetchDouble();
|
||||
testBindFetchText();
|
||||
testBindFetchBlob();
|
||||
testSql();
|
||||
testStatus();
|
||||
testUdf1();
|
||||
testUdfJavaObject();
|
||||
testUdfAggregate();
|
||||
testUdfWindow();
|
||||
testTrace();
|
||||
testProgress();
|
||||
testCommitHook();
|
||||
testRollbackHook();
|
||||
testUpdateHook();
|
||||
testAuthorizer();
|
||||
testAutoExtension();
|
||||
nap(); testOpenDb1();
|
||||
nap(); testOpenDb2();
|
||||
nap(); testCollation();
|
||||
nap(); testPrepare123();
|
||||
nap(); testBindFetchInt();
|
||||
nap(); testBindFetchInt64();
|
||||
nap(); testBindFetchDouble();
|
||||
nap(); testBindFetchText();
|
||||
nap(); testBindFetchBlob();
|
||||
nap(); testSql();
|
||||
nap(); testStatus();
|
||||
nap(); testUdf1();
|
||||
nap(); testUdfJavaObject();
|
||||
nap(); testUdfAggregate();
|
||||
nap(); testUdfWindow();
|
||||
nap(); testTrace();
|
||||
nap(); testProgress();
|
||||
nap(); testCommitHook();
|
||||
nap(); testRollbackHook();
|
||||
nap(); testUpdateHook();
|
||||
nap(); testAuthorizer();
|
||||
nap(); testAutoExtension();
|
||||
if(!fromThread){
|
||||
testBusy();
|
||||
if( !mtMode ){
|
||||
@ -1227,6 +1234,8 @@ public class Tester1 implements Runnable {
|
||||
nThread = Integer.parseInt(args[i++]);
|
||||
}else if(arg.equals("r") || arg.equals("runs")){
|
||||
nRepeat = Integer.parseInt(args[i++]);
|
||||
}else if(arg.equals("naps")){
|
||||
takeNaps = true;
|
||||
}else{
|
||||
throw new IllegalArgumentException("Unhandled flag:"+arg);
|
||||
}
|
||||
@ -1247,7 +1256,7 @@ public class Tester1 implements Runnable {
|
||||
final ExecutorService ex = Executors.newFixedThreadPool( nThread );
|
||||
//final List<Future<?>> futures = new ArrayList<>();
|
||||
++nLoop;
|
||||
out(nLoop+" ");
|
||||
out((1==nLoop ? "" : " ")+nLoop);
|
||||
for( int i = 0; i < nThread; ++i ){
|
||||
ex.submit( new Tester1(i), i );
|
||||
}
|
||||
|
Reference in New Issue
Block a user