Skip useless tasks in the worker to improve fast scrolling with scanned books (bug 1866296)
When a page rendering is cancelled, a task is sent to the worker but before it's executed the rendering task is: the cancel task is more or less useless in this case. So in using the fact that draining the message queue has a higher priority than draining the event one, it's possible to get all the current tasks, hence it's possible to cancel some tasks which are before a cancel task.
This commit is contained in:
parent
76e3e52021
commit
c0ee0ba841
@ -70,6 +70,14 @@ function wrapReason(reason) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MessageHandler {
|
class MessageHandler {
|
||||||
|
#cancelledStreamIds = new Set();
|
||||||
|
|
||||||
|
#executorRunning = false;
|
||||||
|
|
||||||
|
#isPostponed = false;
|
||||||
|
|
||||||
|
#queue = [];
|
||||||
|
|
||||||
constructor(sourceName, targetName, comObj) {
|
constructor(sourceName, targetName, comObj) {
|
||||||
this.sourceName = sourceName;
|
this.sourceName = sourceName;
|
||||||
this.targetName = targetName;
|
this.targetName = targetName;
|
||||||
@ -81,73 +89,118 @@ class MessageHandler {
|
|||||||
this.callbackCapabilities = Object.create(null);
|
this.callbackCapabilities = Object.create(null);
|
||||||
this.actionHandler = Object.create(null);
|
this.actionHandler = Object.create(null);
|
||||||
|
|
||||||
this._onComObjOnMessage = event => {
|
this._onComObjOnMessage = ({ data }) => {
|
||||||
const data = event.data;
|
if (data.targetName === this.sourceName) {
|
||||||
if (data.targetName !== this.sourceName) {
|
// The meesages in the worker queue are processed with a
|
||||||
return;
|
// higher priority than the tasks in the event queue.
|
||||||
}
|
// So, postponing the task execution, will ensure that the message
|
||||||
if (data.stream) {
|
// queue is drained.
|
||||||
this.#processStreamMessage(data);
|
// If at some point we've a cancelled task (e.g. GetOperatorList),
|
||||||
return;
|
// we're able to skip the task execution with the same streamId.
|
||||||
}
|
this.#queue.push(data);
|
||||||
if (data.callback) {
|
this.#isPostponed ||= data.action === "GetOperatorList";
|
||||||
const callbackId = data.callbackId;
|
if (data.stream === StreamKind.CANCEL) {
|
||||||
const capability = this.callbackCapabilities[callbackId];
|
this.#cancelledStreamIds.add(data.streamId);
|
||||||
if (!capability) {
|
|
||||||
throw new Error(`Cannot resolve callback ${callbackId}`);
|
|
||||||
}
|
}
|
||||||
delete this.callbackCapabilities[callbackId];
|
if (!this.#executorRunning) {
|
||||||
|
this.#executorRunning = true;
|
||||||
if (data.callback === CallbackKind.DATA) {
|
this.#postponeExecution();
|
||||||
capability.resolve(data.data);
|
|
||||||
} else if (data.callback === CallbackKind.ERROR) {
|
|
||||||
capability.reject(wrapReason(data.reason));
|
|
||||||
} else {
|
|
||||||
throw new Error("Unexpected callback case");
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const action = this.actionHandler[data.action];
|
|
||||||
if (!action) {
|
|
||||||
throw new Error(`Unknown action from worker: ${data.action}`);
|
|
||||||
}
|
|
||||||
if (data.callbackId) {
|
|
||||||
const cbSourceName = this.sourceName;
|
|
||||||
const cbTargetName = data.sourceName;
|
|
||||||
|
|
||||||
new Promise(function (resolve) {
|
|
||||||
resolve(action(data.data));
|
|
||||||
}).then(
|
|
||||||
function (result) {
|
|
||||||
comObj.postMessage({
|
|
||||||
sourceName: cbSourceName,
|
|
||||||
targetName: cbTargetName,
|
|
||||||
callback: CallbackKind.DATA,
|
|
||||||
callbackId: data.callbackId,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (reason) {
|
|
||||||
comObj.postMessage({
|
|
||||||
sourceName: cbSourceName,
|
|
||||||
targetName: cbTargetName,
|
|
||||||
callback: CallbackKind.ERROR,
|
|
||||||
callbackId: data.callbackId,
|
|
||||||
reason: wrapReason(reason),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data.streamId) {
|
|
||||||
this.#createStreamSink(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
action(data.data);
|
|
||||||
};
|
};
|
||||||
comObj.addEventListener("message", this._onComObjOnMessage);
|
comObj.addEventListener("message", this._onComObjOnMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#postponeExecution() {
|
||||||
|
if (this.#isPostponed) {
|
||||||
|
setTimeout(this.#executor.bind(this), 0);
|
||||||
|
} else {
|
||||||
|
this.#executor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#executor() {
|
||||||
|
if (this.#queue.length === 0) {
|
||||||
|
this.#cancelledStreamIds.clear();
|
||||||
|
this.#executorRunning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = this.#queue.shift();
|
||||||
|
const { stream, streamId } = data;
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
if (
|
||||||
|
stream === StreamKind.CANCEL ||
|
||||||
|
!this.#cancelledStreamIds.has(streamId)
|
||||||
|
) {
|
||||||
|
this.#processStreamMessage(data);
|
||||||
|
}
|
||||||
|
this.#postponeExecution();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamId && this.#cancelledStreamIds.has(streamId)) {
|
||||||
|
this.#postponeExecution();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.callback) {
|
||||||
|
const callbackId = data.callbackId;
|
||||||
|
const capability = this.callbackCapabilities[callbackId];
|
||||||
|
if (!capability) {
|
||||||
|
throw new Error(`Cannot resolve callback ${callbackId}`);
|
||||||
|
}
|
||||||
|
delete this.callbackCapabilities[callbackId];
|
||||||
|
|
||||||
|
if (data.callback === CallbackKind.DATA) {
|
||||||
|
capability.resolve(data.data);
|
||||||
|
} else if (data.callback === CallbackKind.ERROR) {
|
||||||
|
capability.reject(wrapReason(data.reason));
|
||||||
|
} else {
|
||||||
|
throw new Error("Unexpected callback case");
|
||||||
|
}
|
||||||
|
this.#postponeExecution();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = this.actionHandler[data.action];
|
||||||
|
if (!action) {
|
||||||
|
throw new Error(`Unknown action from worker: ${data.action}`);
|
||||||
|
}
|
||||||
|
if (data.callbackId) {
|
||||||
|
const cbSourceName = this.sourceName;
|
||||||
|
const cbTargetName = data.sourceName;
|
||||||
|
|
||||||
|
new Promise(function (resolve) {
|
||||||
|
resolve(action(data.data));
|
||||||
|
}).then(
|
||||||
|
result => {
|
||||||
|
this.comObj.postMessage({
|
||||||
|
sourceName: cbSourceName,
|
||||||
|
targetName: cbTargetName,
|
||||||
|
callback: CallbackKind.DATA,
|
||||||
|
callbackId: data.callbackId,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reason => {
|
||||||
|
this.comObj.postMessage({
|
||||||
|
sourceName: cbSourceName,
|
||||||
|
targetName: cbTargetName,
|
||||||
|
callback: CallbackKind.ERROR,
|
||||||
|
callbackId: data.callbackId,
|
||||||
|
reason: wrapReason(reason),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else if (data.streamId) {
|
||||||
|
this.#createStreamSink(data);
|
||||||
|
} else {
|
||||||
|
action(data.data);
|
||||||
|
}
|
||||||
|
this.#postponeExecution();
|
||||||
|
}
|
||||||
|
|
||||||
on(actionName, handler) {
|
on(actionName, handler) {
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
assert(
|
assert(
|
||||||
|
Loading…
Reference in New Issue
Block a user