mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-06-01 15:51:00 +02:00
Send 'Terminate' to the worker when destroy races the load-time handshake (bug 1942304)
If `PDFDocumentLoadingTask.destroy` ran while `workerIdPromise` was pending, the inner `.then` in `getDocument` threw "Loading aborted" before `WorkerTransport` was constructed, so `_transport` was never set and the "Terminate" message was never posted.
This commit is contained in:
parent
8d3d370daa
commit
cffb19e266
@ -424,9 +424,6 @@ function getDocument(src = {}) {
|
||||
|
||||
Promise.all([worker.promise, gpuPromise])
|
||||
.then(function ([, hasGPU]) {
|
||||
if (task.destroyed) {
|
||||
throw new Error("Loading aborted");
|
||||
}
|
||||
if (worker.destroyed) {
|
||||
throw new Error("Worker was destroyed");
|
||||
}
|
||||
@ -469,9 +466,6 @@ function getDocument(src = {}) {
|
||||
}
|
||||
|
||||
return workerIdPromise.then(workerId => {
|
||||
if (task.destroyed) {
|
||||
throw new Error("Loading aborted");
|
||||
}
|
||||
if (worker.destroyed) {
|
||||
throw new Error("Worker was destroyed");
|
||||
}
|
||||
@ -486,10 +480,18 @@ function getDocument(src = {}) {
|
||||
pagesMapper
|
||||
);
|
||||
task._transport = transport;
|
||||
|
||||
if (task.destroyed) {
|
||||
// `destroy()` was called during the worker handshake; the orderly
|
||||
// shutdown (including the "Terminate" message) will be issued
|
||||
// through the transport once destroy resumes.
|
||||
throw new Error("Loading aborted");
|
||||
}
|
||||
messageHandler.send("Ready", null);
|
||||
});
|
||||
})
|
||||
.catch(task._capability.reject);
|
||||
.catch(task._capability.reject)
|
||||
.finally(task._setupCapability.resolve);
|
||||
|
||||
return task;
|
||||
}
|
||||
@ -515,6 +517,14 @@ class PDFDocumentLoadingTask {
|
||||
*/
|
||||
_capability = Promise.withResolvers();
|
||||
|
||||
/**
|
||||
* Resolves once the load-time setup chain has settled, regardless of
|
||||
* outcome; used by `destroy()` to wait until `_transport` is either set
|
||||
* or definitely never going to be.
|
||||
* @private
|
||||
*/
|
||||
_setupCapability = Promise.withResolvers();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -568,11 +578,25 @@ class PDFDocumentLoadingTask {
|
||||
*/
|
||||
async destroy() {
|
||||
this.destroyed = true;
|
||||
// The setup chain rejects `_capability` with "Loading aborted" once the
|
||||
// load-time chain unwinds (see `getDocument`). Claim that rejection
|
||||
// here so it isn't reported as unhandled during the awaits below;
|
||||
// callers awaiting `task.promise` still see it.
|
||||
this._capability.promise.catch(() => {});
|
||||
|
||||
try {
|
||||
// `_pendingDestroy` must be set synchronously, before any `await`,
|
||||
// so subsequent `PDFWorker.create()` calls on the shared `workerPort`
|
||||
// observe it and throw (see issue 16777).
|
||||
if (this._worker?.port) {
|
||||
this._worker._pendingDestroy = true;
|
||||
}
|
||||
// Wait for the load-time setup chain to settle so `_transport` is set
|
||||
// (when applicable) before we tear down. This is what guarantees the
|
||||
// "Terminate" message gets sent through `WorkerTransport.destroy` if
|
||||
// `destroy` races with the initial worker handshake.
|
||||
await this._setupCapability.promise;
|
||||
|
||||
await this._transport?.destroy();
|
||||
} catch (ex) {
|
||||
if (this._worker?.port) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user