Repository navigation
Conversation
Awaiting a reply from the registering thread inside a resolve or load hook deadlocks: resolution is synchronous from that thread's perspective, so it is blocked in the request and cannot answer. Fixes: nodejs#63085 Signed-off-by: Paul Bouchon <mail@bitpshr.net>
|
Review requested:
|
|
Hi @nodejs/loaders, keeping this on your radar. Doc only: hooks cannot await a reply from the thread that registered them, since that thread is blocked until the hook settles. Green since August. |
| above. It must not wait for a reply while it is running: resolution and loading | ||
| are synchronous from the perspective of the thread that triggered them, so that | ||
| thread is blocked until the hook settles and cannot answer in the meantime. | ||
| Awaiting a reply inside a `resolve` or `load` hook deadlocks both threads. |
There was a problem hiding this comment.
That is not true: @nodejs-loaders/tsx effectively does this (it previously did use async/await before supporting both async and sync).
There was a problem hiding this comment.
@JakobJingleheimer I think we are talking about two different things.
I checked @nodejs-loaders/tsx and the awaiting it does is on esbuild.transform(), a promise that resolves on its own:
return transform(rawSource, { sourcefile: url, ...esbuildConfig })
.catch((f) => handleTrasformErrors(f, url))
.then(({ code: source, warnings }) => {What deadlocks is narrower: awaiting a reply from the thread that registered the hook, over the message channel from the preceding example. That thread is blocked inside the resolution or load it triggered, so it cannot run a listener to answer, and both sides wait forever. That is what the reporter hit in #63085.
So hooks can absolutely be async. The only thing that cannot be awaited is a response from the registering thread. My sentence did not make that scope clear, so how about:
A hook can send messages to the thread that registered it at any time, as above. It must not block on a reply from that thread: resolution and loading are synchronous from the perspective of the thread that triggered them, so that thread is blocked until the hook settles and cannot answer in the meantime. Hooks can otherwise be fully asynchronous; it is only a reply over this message channel that cannot be awaited.
Happy to push that, or to drop it entirely if you think the distinction is too narrow to be worth documenting.
There was a problem hiding this comment.
Ahh. Yes, much cleaner 🙂 thanks!
Fixes: #63085
module.register()documents that hooks can send messages back to the registering thread, and the example does exactly that. What is not documented is that a hook cannot wait for a reply. Resolution and loading are synchronous from the perspective of the thread that triggered them, so that thread is blocked until the hook settles and can never answer in the meantime. Awaiting a reply inside aresolveorloadhook deadlocks both threads.This adds a short paragraph after the messaging example saying so. The issue reporter hit this and it is not obvious from the surrounding text, which reads as though the channel is bidirectional in the usual way.