diff --git a/src/display/api.js b/src/display/api.js index dcbbe2fa7..08b34aa46 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -626,7 +626,17 @@ class PDFDocumentLoadingTask { */ async destroy() { this.destroyed = true; - await this._transport?.destroy(); + try { + if (this._worker?.port) { + this._worker._pendingDestroy = true; + } + await this._transport?.destroy(); + } catch (ex) { + if (this._worker?.port) { + delete this._worker._pendingDestroy; + } + throw ex; + } this._transport = null; if (this._worker) { @@ -2058,10 +2068,6 @@ class PDFWorker { port = null, verbosity = getVerbosityLevel(), } = {}) { - if (port && PDFWorker.#workerPorts.has(port)) { - throw new Error("Cannot use more than one PDFWorker per port."); - } - this.name = name; this.destroyed = false; this.verbosity = verbosity; @@ -2072,6 +2078,9 @@ class PDFWorker { this._messageHandler = null; if (port) { + if (PDFWorker.#workerPorts.has(port)) { + throw new Error("Cannot use more than one PDFWorker per port."); + } PDFWorker.#workerPorts.set(port, this); this._initializeFromPort(port); return; @@ -2287,11 +2296,21 @@ class PDFWorker { * @param {PDFWorkerParameters} params - The worker initialization parameters. */ static fromPort(params) { + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + throw new Error("Not implemented: fromPort"); + } if (!params?.port) { throw new Error("PDFWorker.fromPort - invalid method signature."); } - if (this.#workerPorts.has(params.port)) { - return this.#workerPorts.get(params.port); + const cachedPort = this.#workerPorts.get(params.port); + if (cachedPort) { + if (cachedPort._pendingDestroy) { + throw new Error( + "PDFWorker.fromPort - the worker is being destroyed.\n" + + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls." + ); + } + return cachedPort; } return new PDFWorker(params); } diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 7c14dbee7..6d3f049eb 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -898,6 +898,70 @@ describe("api", function () { }); }); + describe("GlobalWorkerOptions", function () { + let savedGlobalWorkerPort; + + beforeAll(function () { + savedGlobalWorkerPort = GlobalWorkerOptions.workerPort; + }); + + afterAll(function () { + GlobalWorkerOptions.workerPort = savedGlobalWorkerPort; + }); + + it("use global `workerPort` with multiple, sequential, documents", async function () { + if (isNodeJS) { + pending("Worker is not supported in Node.js."); + } + + GlobalWorkerOptions.workerPort = new Worker( + new URL("../../build/generic/build/pdf.worker.js", window.location) + ); + + const loadingTask1 = getDocument(basicApiGetDocumentParams); + const pdfDoc1 = await loadingTask1.promise; + expect(pdfDoc1.numPages).toEqual(3); + await loadingTask1.destroy(); + + const loadingTask2 = getDocument( + buildGetDocumentParams("tracemonkey.pdf") + ); + const pdfDoc2 = await loadingTask2.promise; + expect(pdfDoc2.numPages).toEqual(14); + await loadingTask2.destroy(); + }); + + it( + "avoid using the global `workerPort` when destruction has started, " + + "but not yet finished (issue 16777)", + async function () { + if (isNodeJS) { + pending("Worker is not supported in Node.js."); + } + + GlobalWorkerOptions.workerPort = new Worker( + new URL("../../build/generic/build/pdf.worker.js", window.location) + ); + + const loadingTask = getDocument(basicApiGetDocumentParams); + const pdfDoc = await loadingTask.promise; + expect(pdfDoc.numPages).toEqual(3); + const destroyPromise = loadingTask.destroy(); + + expect(function () { + getDocument(buildGetDocumentParams("tracemonkey.pdf")); + }).toThrow( + new Error( + "PDFWorker.fromPort - the worker is being destroyed.\n" + + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls." + ) + ); + + await destroyPromise; + } + ); + }); + describe("PDFDocument", function () { let pdfLoadingTask, pdfDocument;