diff --git a/dependencies/lmdb/libraries/liblmdb/lmdb.h b/dependencies/lmdb/libraries/liblmdb/lmdb.h
index f3db0d8f5..b258aac06 100644
--- a/dependencies/lmdb/libraries/liblmdb/lmdb.h
+++ b/dependencies/lmdb/libraries/liblmdb/lmdb.h
@@ -339,6 +339,9 @@ typedef int (MDB_enc_func)(const MDB_val *src, MDB_val *dst, const MDB_val *key,
*/
typedef void (MDB_sum_func)(const MDB_val *src, MDB_val *dst, const MDB_val *key);
#endif
+//
+typedef int (MDB_check_fd)(const mdb_filehandle_t fd, MDB_env* env);
+//
/** @defgroup mdb_env Environment Flags
* @{
diff --git a/dependencies/lmdb/libraries/liblmdb/mdb.c b/dependencies/lmdb/libraries/liblmdb/mdb.c
index daf87cdca..c148f132f 100644
--- a/dependencies/lmdb/libraries/liblmdb/mdb.c
+++ b/dependencies/lmdb/libraries/liblmdb/mdb.c
@@ -5872,6 +5872,8 @@ mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl)
MDB_OFF_T size, rsize;
rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd);
+ if (!rc)
+ rc = ((MDB_check_fd*) env->me_userctx)(env->me_lfd, env);
if (rc) {
/* Omit lockfile if read-only env on read-only filesystem */
if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) {
@@ -6300,7 +6302,7 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
env->boot_id = strtoll(boot_uuid, &endptr, 16);
}
#endif
- /**/
+ /**/
env->me_path = strdup(path);
env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
diff --git a/src/env.cpp b/src/env.cpp
index a97033279..0389153f9 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -34,6 +34,39 @@ EnvWrap::EnvWrap(const CallbackInfo& info) : ObjectWrap(info) {
pthread_mutex_init(this->writingLock, nullptr);
pthread_cond_init(this->writingCond, nullptr);
}
+MDB_env* foundEnv;
+const int EXISTING_ENV_FOUND = 10;
+int checkExistingEnvs(mdb_filehandle_t fd, MDB_env* env) {
+ uint64_t inode, dev;
+ #ifdef _WIN32
+ BY_HANDLE_FILE_INFORMATION fileInformation;
+ GetFileInformationByHandle(fd, &fileInformation);
+ dev = fileInformation.dwVolumeSerialNumber;
+ inode = ((uint64_t) fileInformation.nFileIndexHigh << 32) | fileInformation.nFileIndexLow;
+ #else
+ auto st = stat(path);
+ dev = st.st_dev;
+ inode = st.st_ino;
+ #endif
+ fprintf(stderr, "check existing env at dev %llu inode %llu\n", dev, inode);
+ for (auto envRef = EnvWrap::envTracking->envs.begin(); envRef != EnvWrap::envTracking->envs.end();) {
+ if (envRef->dev == dev && envRef->inode == inode) {
+ fprintf(stderr, "found existing env at dev %llu inode %llu\n", dev, inode);
+ envRef->count++;
+ foundEnv = envRef->env;
+ return EXISTING_ENV_FOUND;
+ }
+ ++envRef;
+ }
+ fprintf(stderr, "no existing env at dev %llu inode %llu\n", dev, inode);
+ SharedEnv envRef;
+ envRef.dev = dev;
+ envRef.inode = inode;
+ envRef.env = env;
+ envRef.count = 1;
+ EnvWrap::envTracking->envs.push_back(envRef);
+ return 0;
+}
EnvWrap::~EnvWrap() {
// Close if not closed already
@@ -210,24 +243,10 @@ Napi::Value EnvWrap::open(const CallbackInfo& info) {
}
int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer, Compression* compression, int maxDbs,
int maxReaders, mdb_size_t mapSize, int pageSize, char* encryptionKey) {
- pthread_mutex_lock(envTracking->envsLock);
this->keyBuffer = keyBuffer;
this->compression = compression;
this->jsFlags = jsFlags;
- MDB_env* env = this->env;
- for (auto envPath = envTracking->envs.begin(); envPath != envTracking->envs.end();) {
- char* existingPath = envPath->path;
- if (!strcmp(existingPath, path)) {
- envPath->count++;
- mdb_env_close(env);
- this->env = envPath->env;
- pthread_mutex_unlock(envTracking->envsLock);
- if ((jsFlags & DELETE_ON_CLOSE) || (flags & MDB_OVERLAPPINGSYNC))
- openEnvWraps.push_back(this);
- return 0;
- }
- ++envPath;
- }
+
int rc;
rc = mdb_env_set_maxdbs(env, maxDbs);
if (rc) goto fail;
@@ -244,7 +263,7 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
enckey.mv_data = encryptionKey;
enckey.mv_size = 32;
#ifdef MDB_RPAGE_CACHE
- rc = mdb_env_set_encrypt(this->env, encfunc, &enckey, 0);
+ rc = mdb_env_set_encrypt(env, encfunc, &enckey, 0);
#else
rc = -1;
#endif
@@ -258,24 +277,24 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
if (flags & MDB_NOLOCK) {
fprintf(stderr, "You chose to use MDB_NOLOCK which is not officially supported by node-lmdb. You have been warned!\n");
}
+ mdb_env_set_userctx(env, checkExistingEnvs);
// Set MDB_NOTLS to enable multiple read-only transactions on the same thread (in this case, the nodejs main thread)
flags |= MDB_NOTLS;
// TODO: make file attributes configurable
// *String::Utf8Value(Isolate::GetCurrent(), path)
+ pthread_mutex_lock(envTracking->envsLock);
rc = mdb_env_open(env, path, flags, 0664);
- mdb_env_get_flags(env, (unsigned int*) &flags);
if (rc != 0) {
mdb_env_close(env);
- goto fail;
- }
- SharedEnv envPath;
- envPath.path = strdup(path);
- envPath.env = env;
- envPath.count = 1;
- envPath.deleteOnClose = jsFlags & DELETE_ON_CLOSE;
- envTracking->envs.push_back(envPath);
+ if (rc == EXISTING_ENV_FOUND) {
+ fprintf(stderr, "env_open failed because of existing env %p", foundEnv);
+ env = foundEnv;
+ } else
+ goto fail;
+ }
+ mdb_env_get_flags(env, (unsigned int*) &flags);
if ((jsFlags & DELETE_ON_CLOSE) || (flags & MDB_OVERLAPPINGSYNC))
openEnvWraps.push_back(this);
pthread_mutex_unlock(envTracking->envsLock);
@@ -283,7 +302,7 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
fail:
pthread_mutex_unlock(envTracking->envsLock);
- this->env = nullptr;
+ env = nullptr;
return rc;
}
Napi::Value EnvWrap::getMaxKeySize(const CallbackInfo& info) {
@@ -316,18 +335,11 @@ NAPI_FUNCTION(setJSFlags) {
#endif
-void SharedEnv::finish(bool close) {
-}
NAPI_FUNCTION(EnvWrap::onExit) {
// close all the environments
for (auto envWrap : openEnvWraps) {
envWrap->closeEnv();
}
- /*pthread_mutex_lock(envTracking->envsLock);
- for (auto envPath : envTracking->envs) {
- envPath.finish(false);
- }
- pthread_mutex_unlock(envTracking->envsLock);*/
napi_value returnValue;
RETURN_UNDEFINED;
}
@@ -370,9 +382,13 @@ void EnvWrap::closeEnv() {
if (envFlags & MDB_OVERLAPPINGSYNC) {
mdb_env_sync(env, 1);
}
+ char* path;
+ mdb_env_get_path(env, &((const char*)path));
+ path = strdup(path);
mdb_env_close(env);
- if (envPath->deleteOnClose) {
- unlink(envPath->path);
+ fprintf(stderr, "Closed path %s\n", path);
+ if (jsFlags & DELETE_ON_CLOSE) {
+ unlink(path);
//unlink(strcat(envPath->path, "-lock"));
}
envTracking->envs.erase(envPath);
diff --git a/src/lmdb-js.h b/src/lmdb-js.h
index 3f313645b..853c34a2f 100644
--- a/src/lmdb-js.h
+++ b/src/lmdb-js.h
@@ -183,10 +183,9 @@ class CursorWrap;
class SharedEnv {
public:
MDB_env* env;
- char* path;
+ uint64_t dev;
+ uint64_t inode;
int count;
- bool deleteOnClose;
- void finish(bool close);
};
const int INTERRUPT_BATCH = 9998;
diff --git a/test/index.test.js b/test/index.test.js
index 51761fd25..bbc851907 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -1130,7 +1130,7 @@ describe('lmdb-js', function() {
});
});
});
- describe.only('Read-only Threads', function() {
+ describe('Read-only Threads', function() {
this.timeout(1000000);
it('will run a group of threads with read-only transactions', function(done) {
var child = spawn('node', [fileURLToPath(new URL('./readonly-threads.cjs', import.meta.url))]);
diff --git a/test/readonly-threads.cjs b/test/readonly-threads.cjs
index db49d654d..ad9abb3d7 100644
--- a/test/readonly-threads.cjs
+++ b/test/readonly-threads.cjs
@@ -38,12 +38,8 @@ if (isMainThread) {
setTimeout(() => {
worker.terminate()
- }, 8000);
+ }, 20);
if (messages.length === workerCount) {
- db.close();
- for (var i = 0; i < messages.length; i ++) {
- assert(messages[i] === value.toString('hex'));
- }
console.log("done", threadId)
//setTimeout(() =>
//process.exit(0), 200);
@@ -72,19 +68,16 @@ if (isMainThread) {
if (msg.key) {
var value = db.get(msg.key);
let lastIterate = db.getRange().iterate()
- setInterval(() => {
+ let interval = setInterval(() => {
db.get(msg.key);
let iterate = db.getRange().iterate();
while(!lastIterate.next().done){}
lastIterate = iterate;
}, 1);
setTimeout(() => {
- if (value === null) {
+ clearInterval(interval)
parentPort.postMessage("");
- } else {
- parentPort.postMessage(value.toString('hex'));
- }
- }, 10000);
+ }, 100);
}
});