Skip to content

Commit 00459a1

Browse files
fix: race condition when importing AsyncLocalStorage (#17350)
* fix: race condition when importing `AsyncLocalStorage` * types: * tweak * remove a line * comment * dumb esbuild * final final pt2 final * ugh
1 parent a544a9d commit 00459a1

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

.changeset/early-showers-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: race condition when importing `AsyncLocalStorage`

packages/svelte/src/internal/server/render-context.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/** @import { AsyncLocalStorage } from 'node:async_hooks' */
33
/** @import { RenderContext } from '#server' */
44

5-
import { deferred } from '../shared/utils.js';
5+
import { deferred, noop } from '../shared/utils.js';
66
import * as e from './errors.js';
77

88
/** @type {Promise<void> | null} */
@@ -56,14 +56,26 @@ export async function with_render_context(fn) {
5656

5757
/** @type {AsyncLocalStorage<RenderContext | null> | null} */
5858
let als = null;
59+
/** @type {Promise<void> | null} */
60+
let als_import = null;
5961

60-
export async function init_render_context() {
61-
if (als !== null) return;
62-
try {
63-
// @ts-ignore -- we don't include node types in the production build
64-
const { AsyncLocalStorage } = await import('node:async_hooks');
65-
als = new AsyncLocalStorage();
66-
} catch {}
62+
/**
63+
*
64+
* @returns {Promise<void>}
65+
*/
66+
export function init_render_context() {
67+
// It's important the right side of this assignment can run a maximum of one time
68+
// otherwise it's possible for a very, very well-timed race condition to assign to `als`
69+
// at the beginning of a render, and then another render to assign to it again, which causes
70+
// the first render's second half to use a new instance of `als` which doesn't have its
71+
// context anymore.
72+
// @ts-ignore -- we don't include node types in the production build
73+
als_import ??= import('node:async_hooks')
74+
.then((hooks) => {
75+
als = new hooks.AsyncLocalStorage();
76+
})
77+
.then(noop, noop);
78+
return als_import;
6779
}
6880

6981
// this has to be a function because rollup won't treeshake it if it's a constant

packages/svelte/tests/runtime-browser/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ async function run_test(
164164
}
165165
],
166166
bundle: true,
167+
platform: 'node',
167168
format: 'iife',
168169
globalName: 'test_ssr'
169170
});

0 commit comments

Comments
 (0)