Skip to content

fix(signals): keep latest readers out of loading lanes - #3294

Closed
javascript-unsafe wants to merge 1 commit into
solidjs:nextfrom
javascript-unsafe:fix/latest-loading-lane-3289
Closed

fix(signals): keep latest readers out of loading lanes#3294
javascript-unsafe wants to merge 1 commit into
solidjs:nextfrom
javascript-unsafe:fix/latest-loading-lane-3289

Conversation

@javascript-unsafe

Copy link
Copy Markdown

Summary

latest() readers could stay on an old value while an outer <Loading> boundary was waiting for several async memos.

The problem was that the optimistic lane also adopted latest()'s _latestValueComputed companion. That companion is a render reader, not async work that should block the reader. The fix stops adopting it, while keeping _pendingSignal lane adoption unchanged.

A regression test covers three async memos, a loading boundary, and three consecutive reloads.

Fixes #3289

How did you test?

  • pnpm build
  • pnpm test
  • cd packages/signals && pnpm types
  • Targeted signals and Solid integration tests
  • Prettier check on changed files
  • Browser reproduction harness with repeated Reload clicks

All completed successfully. The commit hook's repository-wide formatter step still fails on existing JSX fixture syntax in packages/babel-plugin/test/*fixtures*/insertChildren; no fixture files are part of this PR.

@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 28816eb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@javascript-unsafe
javascript-unsafe force-pushed the fix/latest-loading-lane-3289 branch from 8a911a0 to 28816eb Compare September 6, 2026 07:27
@codspeed-hq

codspeed-hq Bot commented Sep 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 136 untouched benchmarks


Comparing javascript-unsafe:fix/latest-loading-lane-3289 (28816eb) with next (b3c94be)

Open in CodSpeed

@ryansolid

Copy link
Copy Markdown
Member

Thanks for taking this on. Before we can review the lane change, the PR needs a test that fails without it — and this one doesn't. I ran the new latest-async case against current next with lanes.ts untouched and it passes; I also ran four variants of the same shape (tab reader as a render effect and as a user effect, boundary reading the tab memo or not) and every write while the three async memos were still pending landed immediately ([0, 1, 2, 3]), no fix applied.

That matches what the deleted line can do. adoptCompanionLane returns early unless the companion already has its own lane, and when it does, re-parenting it under the owner is what lets the companion's readers flush on their own instead of being merged into the owner's async (#2887 — the comment above it describes exactly that). Removing it is a no-op in the common case and, in the case where it fires, moves in the wrong direction. So even if the engine had the bug, this wouldn't be the mechanism.

If your browser harness reproduces #3289 consistently, that's the valuable part — the divergence from the engine-level shape suggests the hold is at a different layer (component <Loading>, the merge-based withDefaults, or something in the repro's structure). Could you reduce the harness to the smallest tree that still shows the stale TabPanel, and turn that into a failing test (packages/web or packages/solid is fine if it needs components)? A failing test is what turns this from a plausible fix into a reviewable one.

Leaving open for that; #3289 stays open on our side too.

Claude via Cursor

@javascript-unsafe

Copy link
Copy Markdown
Author

The bug is not in the engine layer lanes, but in the component/application layer: the common owner structure generated byboundary+withDefaults (merge) causes the rendering of TabPanel to be merged into the held transition of Loading. This explains why engine level testing cannot detect it, while real apps (including merge props) can reproduce it.

@javascript-unsafe

Copy link
Copy Markdown
Author

Thank you. I have verified the repair of this PR through rigorous A/B control experiments and confirmed that it is no op (invalid but no regression). Therefore, I am voluntarily closing this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2.0.0-rc.6 | render is blocking on a latest signal read.

2 participants