Skip to content

Commit

Permalink
fix(@angular/build): avoid race condition in sass importer
Browse files Browse the repository at this point in the history
On slow systems, a race condition can lead to the sass worker thread
being notified to wake up before a message is posted. This causes the
build to be aborted because the searched file is not found.

Waiting twice for a non-zero number in the signal handles this race
correctly, and the second wait should be a noop in the usual case.

Fixes #27167
  • Loading branch information
hborchardt authored and jkrems committed Oct 15, 2024
1 parent d8ab36b commit 5f473af
Showing 1 changed file with 19 additions and 0 deletions.
19 changes: 19 additions & 0 deletions packages/angular/build/src/tools/sass/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,25 @@ export default async function renderSassStylesheet(
containingUrl: containingUrl ? fileURLToPath(containingUrl) : null,
},
});
// Wait for the main thread to set the signal to 1 and notify, which tells
// us that a message can be received on the port.
// If the main thread is fast, the signal will already be set to 1, and no
// sleep/notify is necessary.
// However, there can be a race condition here:
// - the main thread sets the signal to 1, but does not get to the notify instruction yet
// - the worker does not pause because the signal is set to 1
// - the worker very soon enters this method again
// - this method sets the signal to 0 and sends the message
// - the signal is 0 and so the `Atomics.wait` call blocks
// - only now the main thread runs the `notify` from the first invocation, so the
// worker continues.
// - but there is no message yet in the port, because the thread should not have been
// waken up yet.
// To combat this, wait for a non-0 value _twice_.
// Almost every time, this immediately continues with "not-equal", because
// the signal is still set to 1, except during the race condition, when the second
// wait will wait for the correct notify.
Atomics.wait(importerChannel.signal, 0, 0);
Atomics.wait(importerChannel.signal, 0, 0);

const result = receiveMessageOnPort(importerChannel.port)?.message as string | null;
Expand Down

0 comments on commit 5f473af

Please sign in to comment.