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:
Calixte Denizet 2023-12-10 17:55:06 +01:00
parent 76e3e52021
commit c0ee0ba841

View File

@ -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,15 +89,62 @@ 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
// higher priority than the tasks in the event queue.
// So, postponing the task execution, will ensure that the message
// queue is drained.
// If at some point we've a cancelled task (e.g. GetOperatorList),
// we're able to skip the task execution with the same streamId.
this.#queue.push(data);
this.#isPostponed ||= data.action === "GetOperatorList";
if (data.stream === StreamKind.CANCEL) {
this.#cancelledStreamIds.add(data.streamId);
}
if (!this.#executorRunning) {
this.#executorRunning = true;
this.#postponeExecution();
}
}
};
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; return;
} }
if (data.stream) {
const data = this.#queue.shift();
const { stream, streamId } = data;
if (stream) {
if (
stream === StreamKind.CANCEL ||
!this.#cancelledStreamIds.has(streamId)
) {
this.#processStreamMessage(data); this.#processStreamMessage(data);
}
this.#postponeExecution();
return; return;
} }
if (streamId && this.#cancelledStreamIds.has(streamId)) {
this.#postponeExecution();
return;
}
if (data.callback) { if (data.callback) {
const callbackId = data.callbackId; const callbackId = data.callbackId;
const capability = this.callbackCapabilities[callbackId]; const capability = this.callbackCapabilities[callbackId];
@ -105,6 +160,7 @@ class MessageHandler {
} else { } else {
throw new Error("Unexpected callback case"); throw new Error("Unexpected callback case");
} }
this.#postponeExecution();
return; return;
} }
const action = this.actionHandler[data.action]; const action = this.actionHandler[data.action];
@ -118,8 +174,8 @@ class MessageHandler {
new Promise(function (resolve) { new Promise(function (resolve) {
resolve(action(data.data)); resolve(action(data.data));
}).then( }).then(
function (result) { result => {
comObj.postMessage({ this.comObj.postMessage({
sourceName: cbSourceName, sourceName: cbSourceName,
targetName: cbTargetName, targetName: cbTargetName,
callback: CallbackKind.DATA, callback: CallbackKind.DATA,
@ -127,8 +183,8 @@ class MessageHandler {
data: result, data: result,
}); });
}, },
function (reason) { reason => {
comObj.postMessage({ this.comObj.postMessage({
sourceName: cbSourceName, sourceName: cbSourceName,
targetName: cbTargetName, targetName: cbTargetName,
callback: CallbackKind.ERROR, callback: CallbackKind.ERROR,
@ -137,15 +193,12 @@ class MessageHandler {
}); });
} }
); );
return; } else if (data.streamId) {
}
if (data.streamId) {
this.#createStreamSink(data); this.#createStreamSink(data);
return; } else {
}
action(data.data); action(data.data);
}; }
comObj.addEventListener("message", this._onComObjOnMessage); this.#postponeExecution();
} }
on(actionName, handler) { on(actionName, handler) {