Repository navigation
Give async bodies and generators 512 KiB stacks - #807
Merged
Merged
Conversation
Async functions and generators run on their own fiber stacks. The new stack-overflow guard keeps a quarter of each stack as a reserve for unwinding and error reporting, which cut the depth they could recurse to from 256 KiB to 192 KiB of frames. Moderately deep recursion inside an async body or generator then threw RangeError where Node runs it, for example 3000 frames of an unoptimized build on arm64 macOS. Fibers now get 512 KiB, which leaves 384 KiB after the reserve. Pages are still committed lazily, so idle and shallow fibers cost address space, not memory. The corpus pins recursion inside async functions and generators next to deeper recursion on the main stack.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The stack guard was a tentative definition, which some targets emit as a common symbol. Localized library archives deliberately leave common symbols shared, so on macOS the archive defined scr_stack_guard alongside its declared exports. An explicit initializer makes it an ordinary definition that localization hides like the rest of the runtime.
Embedded engine calls entered from a fiber get a stack budget documented as half the fiber stack, leaving the other half as margin. The budget stayed at 128 KiB when fibers grew to 512 KiB, so it is now 256 KiB, which restores that ratio.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Async functions and generators run on fiber stacks of their own. The stack-overflow guard keeps a quarter of each stack in reserve for unwinding and error reporting. On 256 KiB fibers that left 192 KiB of frames, so recursion that Node runs fine inside an async body or generator threw
RangeError: Maximum call stack size exceeded. On main this failstests/corpus/native-worker-stack-overflow.tson macOS arm64 in dev builds, where 3000 unoptimized frames of the worker's top-level code (an async module body) no longer fit. The same thing reproduces on Linux at greater depth: Node runs 8000 frames inside an async function, while the native build threw.limitations.mdxis updated.stack-depth-async-bodies.tspins recursion inside async functions and generators, alongside deeper recursion on the main stack.Measured with 20,000 concurrent async functions each awaiting a timer (release, median of 9 alternating runs; stack pages are still committed lazily):
512 KiB keeps fiber creation and teardown cost within timer resolution. 1 MiB cost about 13% on this fiber-churn workload, likely from page-table overhead for sparser mappings, so 512 KiB is the better trade.
Validation: 380 async, await, generator, promise, worker, timer, island and stack programs pass in both differential lanes with a runtime pack built from this tree. Runtime tests and runtime-tree-shaking pass, apart from the string oracle, which is environment-only. TypeScript preflight/order baselines are complete and the new batch passes. Lint and format are clean.