From 6d9b2cdbdd802ab625e483d28aaf2eaa43b9c90b Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Thu, 4 Dec 2025 09:00:29 +0200 Subject: [PATCH 1/3] fix access handle deadlock --- .changeset/fluffy-plums-carry.md | 5 +++++ src/examples/OPFSCoopSyncVFS.js | 36 ++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 .changeset/fluffy-plums-carry.md diff --git a/.changeset/fluffy-plums-carry.md b/.changeset/fluffy-plums-carry.md new file mode 100644 index 00000000..4b1b2161 --- /dev/null +++ b/.changeset/fluffy-plums-carry.md @@ -0,0 +1,5 @@ +--- +'@journeyapps/wa-sqlite': patch +--- + +Fix potential deadlocks and Failed to execute 'createSyncAccessHandle' on 'FileSystemFileHandle' errors. diff --git a/src/examples/OPFSCoopSyncVFS.js b/src/examples/OPFSCoopSyncVFS.js index 90630e6f..36d8e6f8 100644 --- a/src/examples/OPFSCoopSyncVFS.js +++ b/src/examples/OPFSCoopSyncVFS.js @@ -519,17 +519,31 @@ export class OPFSCoopSyncVFS extends FacadeVFS { this._module.retryOps.push((async () => { // Acquire the Web Lock. file.persistentFile.handleLockReleaser = await this.#acquireLock(file.persistentFile); - - // Get access handles for the database and releated files in parallel. - this.log?.(`creating access handles for ${file.path}`) - await Promise.all(DB_RELATED_FILE_SUFFIXES.map(async suffix => { - const persistentFile = this.persistentFiles.get(file.path + suffix); - if (persistentFile) { - persistentFile.accessHandle = - await persistentFile.fileHandle.createSyncAccessHandle(); - } - })); - file.persistentFile.isRequestInProgress = false; + try { + // Get access handles for the database and releated files in parallel. + this.log?.(`creating access handles for ${file.path}`) + await Promise.all(DB_RELATED_FILE_SUFFIXES.map(async suffix => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle = + await persistentFile.fileHandle.createSyncAccessHandle(); + } + })); + } catch (e) { + this.log?.(`failed to create access handles for ${file.path}`, e); + // Close any of the potentially opened access handles + DB_RELATED_FILE_SUFFIXES.forEach(async suffix => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle?.close(); + } + }); + // Release the lock, if we failed here, we'd need to obtain the lock later in order to retry + file.persistentFile.handleLockReleaser(); + throw e; + } finally { + file.persistentFile.isRequestInProgress = false; + } })()); return this._module.retryOps.at(-1); } From 355cd277a248d982bed1f5d20104834a8c7d8a9f Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Thu, 4 Dec 2025 09:29:50 +0200 Subject: [PATCH 2/3] set the releaser to null for good measure --- src/examples/OPFSCoopSyncVFS.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/examples/OPFSCoopSyncVFS.js b/src/examples/OPFSCoopSyncVFS.js index 36d8e6f8..716ecbcf 100644 --- a/src/examples/OPFSCoopSyncVFS.js +++ b/src/examples/OPFSCoopSyncVFS.js @@ -540,6 +540,7 @@ export class OPFSCoopSyncVFS extends FacadeVFS { }); // Release the lock, if we failed here, we'd need to obtain the lock later in order to retry file.persistentFile.handleLockReleaser(); + file.persistentFile.handleLockReleaser = null; throw e; } finally { file.persistentFile.isRequestInProgress = false; From 7516136a69d26b4e49a2a5fddc40de05e3a516b0 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Thu, 4 Dec 2025 14:11:09 +0200 Subject: [PATCH 3/3] cleanup. call releaseAccessHandle on error --- src/examples/OPFSCoopSyncVFS.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/examples/OPFSCoopSyncVFS.js b/src/examples/OPFSCoopSyncVFS.js index 716ecbcf..33d32cc8 100644 --- a/src/examples/OPFSCoopSyncVFS.js +++ b/src/examples/OPFSCoopSyncVFS.js @@ -532,15 +532,7 @@ export class OPFSCoopSyncVFS extends FacadeVFS { } catch (e) { this.log?.(`failed to create access handles for ${file.path}`, e); // Close any of the potentially opened access handles - DB_RELATED_FILE_SUFFIXES.forEach(async suffix => { - const persistentFile = this.persistentFiles.get(file.path + suffix); - if (persistentFile) { - persistentFile.accessHandle?.close(); - } - }); - // Release the lock, if we failed here, we'd need to obtain the lock later in order to retry - file.persistentFile.handleLockReleaser(); - file.persistentFile.handleLockReleaser = null; + this.#releaseAccessHandle(file); throw e; } finally { file.persistentFile.isRequestInProgress = false;