Skip to content

[script-compiler] Mapping sent and received messages and avoiding race conditions #72

@otariidae

Description

@otariidae

I'd like to have a one-to-one correspondence between the text I send to the Textlint worker and the lint result I receive from the Textlint worker.
However, the order I received results from the Textlint worker can differ from the one I posted texts to the worker probably because kernel.lintText is async. Ref:

return kernel.lintText(data.text, {
rules: rules,
filterRules: config.filterRules,
plugins: config.plugins,
filePath: "/path/to/README" + data.ext,
ext: data.ext,
}).then(result => {
return self.postMessage({
command: "lint:result",
result
});
});

So relying on message reception order can cause a race condition. Pseudocode:

worker.addEventListener("message", (event) => {
  // lint result of text#2 comes first
  // lint result of text#1 comes next
})
worker.postMessage({
  command: "lint",
  text: "text#1 very long text that takes a long time to lint",
  ext: ".txt"
})
worker.postMessage({
  command: "lint",
  text: "text#2 short text; lint finishes quickly",
  ext: ".txt"
})

More higher-level example:

const lintText: LintEngineAPI["lintText"] = async ({ text }: { text: string }): Promise<TextlintResult[]> => {
updateStatus("linting...");
return new Promise((resolve, _reject) => {
worker.addEventListener(
"message",
function (event) {
const data: TextlintWorkerCommandResponse = event.data;
if (data.command === "lint:result") {
resolve([data.result]);
}
updateStatus("linted");
},
{
once: true
}
);
return worker.postMessage({
command: "lint",
text,
ext: ext
} as TextlintWorkerCommandLint);
});
};

const texts = ["text#1 very long text that takes a long time to lint", "text#2 short text; lint finishes quickly"]
const results = await Promise.all(texts => lintText({ text }))
// [<lint result of text#2>, <lint result of text#2>]
// lint result of text#1 goes away

Some Web Worker libraries use IDs in messages. I think it can be one of the ideas to solve this problem.
For example, comlink:

https://github.com/GoogleChromeLabs/comlink/blob/dffe9050f63b1b39f30213adeb1dd4b9ed7d2594/src/comlink.ts#L596-L615

For another example, minlink:

https://github.com/mizchi/minlink/blob/98f0eed1b1fc00a51709e07c6fe3e18232cdfaad/src/shared.ts#L52-L61

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions