Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spawn new task if we hit stack overflow #30419

Merged
merged 1 commit into from
Aug 27, 2024

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Jul 22, 2024

If we see the "Maximum call stack size exceeded" error we know we've hit stack overflow. We can recover from this by spawning a new task and trying again. Effectively a zero-cost trampoline in the normal case. The new task will have a clean stack. If you have a lot of siblings at the same depth that hits the limit you can end up hitting this once for each sibling but within that new sibling you're unlikely to hit this again. So it's not too expensive.

If it errors again in the retryTask pass, the other error handling takes over which causes this to be able to still not infinitely stall. E.g. when the component itself throws an error like this.

It's still better to increase the stack limit for performance if you have a really deep tree but it doesn't really hurt to be able to recover since it's zero cost when it doesn't happen.

We could do the same thing for Flight. Those trees don't tend to be as deep but could happen.

If we see the "Maximum call stack size exceeded" error we know we've hit
stack overflow. We can recover from this by spawning a new task and trying
again. Effectively a zero-cost trampoline in the normal case.

If it errors again in the retryTask pass, the other error handling takes
over which causes this to be able to still not infinitely stall.
@sebmarkbage sebmarkbage requested a review from kassens July 22, 2024 19:14
Copy link

vercel bot commented Jul 22, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 22, 2024 7:16pm

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jul 22, 2024
switchContext(previousContext);
return;
}
if (x.message === 'Maximum call stack size exceeded') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 and Hermes has the same message.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log some warning here? Is there any way product developers can action on this / any other downsides to having trees deep enough to hit this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it's actionable. If you have a large site like facebook you're likely to have giant trees. However, you might also not have the ability to increase the stack limit of the service you're using.

@react-sizebot
Copy link

Comparing: d7c4334...414f9e0

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.82 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 499.44 kB 499.44 kB = 89.59 kB 89.59 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 504.26 kB 504.26 kB = 90.30 kB 90.30 kB
facebook-www/ReactDOM-prod.classic.js = 599.20 kB 599.20 kB = 105.79 kB 105.79 kB
facebook-www/ReactDOM-prod.modern.js = 573.37 kB 573.37 kB = 101.68 kB 101.68 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-rc/react-server/cjs/react-server.production.js +1.29% 104.13 kB 105.47 kB +0.64% 19.37 kB 19.49 kB
oss-stable-semver/react-server/cjs/react-server.production.js +1.29% 104.13 kB 105.47 kB +0.64% 19.37 kB 19.49 kB
oss-stable/react-server/cjs/react-server.production.js +1.29% 104.13 kB 105.47 kB +0.64% 19.37 kB 19.49 kB
oss-experimental/react-server/cjs/react-server.production.js +1.12% 114.34 kB 115.62 kB +0.36% 20.90 kB 20.98 kB
oss-stable-rc/react-server/cjs/react-server.development.js +0.92% 153.21 kB 154.62 kB +0.37% 28.28 kB 28.39 kB
oss-stable-semver/react-server/cjs/react-server.development.js +0.92% 153.21 kB 154.62 kB +0.37% 28.28 kB 28.39 kB
oss-stable/react-server/cjs/react-server.development.js +0.92% 153.21 kB 154.62 kB +0.37% 28.28 kB 28.39 kB
oss-experimental/react-server/cjs/react-server.development.js +0.84% 169.74 kB 171.17 kB +0.27% 30.72 kB 30.81 kB
facebook-www/ReactDOMServer-prod.classic.js +0.61% 207.46 kB 208.73 kB +0.29% 38.08 kB 38.19 kB
oss-stable-rc/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.59% 197.52 kB 198.68 kB +0.25% 36.66 kB 36.75 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.59% 197.52 kB 198.68 kB +0.25% 36.66 kB 36.75 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.59% 197.54 kB 198.70 kB +0.25% 36.68 kB 36.77 kB
oss-stable-rc/react-dom/cjs/react-dom-server.bun.production.js +0.58% 201.81 kB 202.98 kB +0.26% 37.83 kB 37.92 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.js +0.58% 201.81 kB 202.98 kB +0.26% 37.83 kB 37.92 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.js +0.58% 201.88 kB 203.04 kB +0.26% 37.85 kB 37.95 kB
oss-stable-rc/react-dom/cjs/react-dom-server-legacy.node.production.js +0.58% 201.91 kB 203.07 kB +0.27% 38.39 kB 38.49 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.js +0.58% 201.91 kB 203.07 kB +0.27% 38.39 kB 38.49 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.js +0.58% 201.93 kB 203.10 kB +0.27% 38.41 kB 38.51 kB
facebook-www/ReactDOMServer-prod.modern.js +0.57% 205.20 kB 206.36 kB +0.27% 37.74 kB 37.84 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +0.56% 209.73 kB 210.89 kB +0.27% 39.27 kB 39.38 kB
oss-experimental/react-html/cjs/react-html.production.js +0.55% 199.96 kB 201.07 kB +0.18% 37.81 kB 37.88 kB
oss-stable-rc/react-dom/cjs/react-dom-server.browser.production.js +0.55% 213.32 kB 214.49 kB +0.23% 39.04 kB 39.13 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.js +0.55% 213.32 kB 214.49 kB +0.23% 39.04 kB 39.13 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.js +0.55% 213.39 kB 214.55 kB +0.23% 39.06 kB 39.15 kB
oss-stable-rc/react-dom/cjs/react-dom-server.node.production.js +0.55% 214.04 kB 215.21 kB +0.25% 39.63 kB 39.73 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.js +0.55% 214.04 kB 215.21 kB +0.25% 39.63 kB 39.73 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.js +0.55% 214.11 kB 215.27 kB +0.25% 39.66 kB 39.76 kB
oss-stable-rc/react-dom/cjs/react-dom-server.edge.production.js +0.54% 218.20 kB 219.37 kB +0.25% 40.90 kB 41.01 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.js +0.54% 218.20 kB 219.37 kB +0.25% 40.90 kB 41.01 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.js +0.54% 218.26 kB 219.43 kB +0.24% 40.93 kB 41.03 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.52% 211.76 kB 212.86 kB +0.14% 38.64 kB 38.69 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.js +0.51% 216.70 kB 217.81 kB +0.14% 40.47 kB 40.53 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.js +0.51% 217.72 kB 218.83 kB +0.13% 39.93 kB 39.98 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.js +0.47% 237.56 kB 238.67 kB +0.12% 41.94 kB 41.99 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.js +0.46% 238.92 kB 240.03 kB +0.12% 43.16 kB 43.21 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.js +0.46% 242.99 kB 244.10 kB +0.12% 43.92 kB 43.97 kB
facebook-www/ReactDOMServer-dev.classic.js +0.43% 357.79 kB 359.31 kB +0.17% 65.22 kB 65.33 kB
oss-experimental/react-html/cjs/react-html.development.js +0.42% 337.89 kB 339.32 kB +0.13% 61.83 kB 61.91 kB
oss-stable-rc/react-dom/cjs/react-dom-server-legacy.node.development.js +0.42% 341.11 kB 342.53 kB +0.19% 62.78 kB 62.90 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.42% 341.11 kB 342.53 kB +0.19% 62.78 kB 62.90 kB
oss-stable-rc/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.42% 341.12 kB 342.53 kB +0.19% 62.78 kB 62.90 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.42% 341.12 kB 342.53 kB +0.19% 62.78 kB 62.90 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.42% 341.14 kB 342.55 kB +0.20% 62.80 kB 62.92 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.42% 341.14 kB 342.55 kB +0.19% 62.80 kB 62.92 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.41% 343.63 kB 345.04 kB +0.19% 62.94 kB 63.07 kB
oss-stable-rc/react-dom/cjs/react-dom-server.node.development.js +0.41% 348.38 kB 349.80 kB +0.19% 63.39 kB 63.52 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.41% 348.38 kB 349.80 kB +0.19% 63.39 kB 63.52 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.41% 348.44 kB 349.86 kB +0.19% 63.42 kB 63.54 kB
facebook-www/ReactDOMServer-dev.modern.js +0.40% 351.05 kB 352.47 kB +0.19% 64.10 kB 64.23 kB
oss-stable-rc/react-dom/cjs/react-dom-server.browser.development.js +0.40% 353.27 kB 354.69 kB +0.19% 64.70 kB 64.82 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.40% 353.27 kB 354.69 kB +0.19% 64.70 kB 64.82 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.40% 353.34 kB 354.75 kB +0.19% 64.72 kB 64.85 kB
oss-stable-rc/react-dom/cjs/react-dom-server.edge.development.js +0.40% 353.82 kB 355.24 kB +0.20% 64.80 kB 64.93 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.40% 353.82 kB 355.24 kB +0.20% 64.80 kB 64.93 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.40% 353.88 kB 355.30 kB +0.19% 64.83 kB 64.95 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.39% 365.80 kB 367.23 kB +0.12% 65.96 kB 66.04 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.39% 365.80 kB 367.23 kB +0.12% 65.96 kB 66.04 kB
oss-stable-rc/react-dom/cjs/react-dom-server.bun.development.js +0.38% 303.98 kB 305.15 kB +0.16% 59.79 kB 59.89 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.38% 303.98 kB 305.15 kB +0.16% 59.79 kB 59.89 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.38% 304.04 kB 305.21 kB +0.16% 59.81 kB 59.91 kB
oss-experimental/react-html/cjs/react-html.react-server.production.js +0.37% 295.65 kB 296.76 kB +0.10% 56.13 kB 56.19 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.37% 384.23 kB 385.66 kB +0.12% 68.20 kB 68.27 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.37% 388.77 kB 390.20 kB +0.11% 68.69 kB 68.77 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.37% 389.32 kB 390.75 kB +0.11% 68.78 kB 68.86 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.36% 326.93 kB 328.10 kB +0.10% 63.01 kB 63.07 kB
oss-experimental/react-html/cjs/react-html.react-server.development.js +0.29% 485.63 kB 487.06 kB +0.10% 87.89 kB 87.98 kB

Generated by 🚫 dangerJS against 16b5e3b

@tom-sherman
Copy link
Contributor

tom-sherman commented Jul 23, 2024

Functions that recurse forever now freeze the page (repro). This is super hard to debug because practically it's just difficult to open the dev tools in this case.

X ref.

@sebmarkbage
Copy link
Collaborator Author

@tom-sherman That might need a limit like the useEffect loop in Fiber but that's also already a concern with recursive async ones.

@tom-sherman
Copy link
Contributor

I think it's not quite a severe DX regression with async components because it's more trivial to kill and restart (no state) and their async-ness means it's easier to attach a debugger between tasks.

@sebmarkbage
Copy link
Collaborator Author

I think it's not quite a severe DX regression with async components because it's more trivial to kill and restart (no state) and their async-ness means it's easier to attach a debugger between tasks.

Only if they actually have I/O but if it's just an async function with nothing in it, it still stalls because microtasks don't yield.

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Jul 23, 2024

Well actually maybe our Fizz implementation current still yields in that case? I think maybe Flight doesn't anymore or something like that. EDIT: No it's the reverse. I don't think Fizz yields to macrotasks.

@tom-sherman
Copy link
Contributor

Ah I just realised this PR is server 🤦

In that case, it's pretty bad to hang the server forever I think? 😅 In a serverless environment it's bad also because you'll get a timeout and not even get a stack trace in some cases.

@sebmarkbage
Copy link
Collaborator Author

Yea but regardless that's already something you can do. It's a halting problem - not a stack overflow problem.

@sebmarkbage sebmarkbage merged commit 96aca5f into facebook:main Aug 27, 2024
187 checks passed
github-actions bot pushed a commit that referenced this pull request Aug 27, 2024
If we see the "Maximum call stack size exceeded" error we know we've hit
stack overflow. We can recover from this by spawning a new task and
trying again. Effectively a zero-cost trampoline in the normal case. The
new task will have a clean stack. If you have a lot of siblings at the
same depth that hits the limit you can end up hitting this once for each
sibling but within that new sibling you're unlikely to hit this again.
So it's not too expensive.

If it errors again in the retryTask pass, the other error handling takes
over which causes this to be able to still not infinitely stall. E.g.
when the component itself throws an error like this.

It's still better to increase the stack limit for performance if you
have a really deep tree but it doesn't really hurt to be able to recover
since it's zero cost when it doesn't happen.

We could do the same thing for Flight. Those trees don't tend to be as
deep but could happen.

DiffTrain build for [96aca5f](96aca5f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants