Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[2.0 / next] `probing` leaks into a memo's pass pulled inside `isPending` — a nested `latest()` joins the pass to the hold

Đã đóng
#4,429 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
typescript
Lĩnh vực
frontend

Hướng nghiên cứu

Start with the reader-seat rebuild in issue #4395 and the S1 PR, which the issue identifies as deciding a pulled pass’s world from its seat and the read’s posture. Reproduce with async-gen seed 7, case #1691, in packages/web/test/async-gen. Trace how verdict, probing, and probeFound are handled around a pulled memo’s pass; done means the pass’s world and probe answer reflect its own reads, not the outer isPending window.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Summary

isPending(fn) sets the module flag probing for the duration of fn. When a read inside fn pulls a dirty memo, verdictValue closes the window for the memo's pass (setVerdict(null) — A31: "the pulled memo's pass is its own, not the probe's") but leaves probing set. If that pass calls latest() itself, latest re-opens verdictValue with probing still true, and the pulled memo's read is treated as the outer probe's:

  • a held node born into the future (CONFIG_HELD, uninitialized, staged) read through latest() takes the probing && owned arm and joinPasses the pulled memo to the hold (REACTIVE_JOINED) — the memo's pass joins a world its own read (latest, a verdict read) does not derive from;
  • probeFound is set from inside the pulled pass (heldNotFinal, flying, a live affects() mark), so the outer isPending answers for reads the pulled memo made, not for the memo it read.

The same memo pulled by a plain read (or already clean) does neither. The pass's world depends on who woke it.

Reproduction

async-gen seed 7 case #1691 (packages/web/test/async-gen):

const sc = fc.sample(scenarioArb(), { seed: 7, numRuns: 1692 })[1691];
await runScenario(sc);
[s0=0 m0=memo(s0) m1=memo(latest(m0)+m0 !%4=1) m2=memo(s0 !%4=2)]
<Show when=latest(m2) fb>
  <span split[isPending(m0),isPending(s0)]>m0()</span>
  <span split[latest(s0)]>isPending(m1)</span>
</Show>
:: t A(s0=1) s0=2 s0=3 f s0=0 t s0=1 s0=3 m t m
scenario JSON
{"signals":[0],"memos":[{"async":false,"deps":[{"src":{"k":"s","i":0},"how":"plain"}],"poison":null},{"async":false,"deps":[{"src":{"k":"m","i":0},"how":"latest"},{"src":{"k":"m","i":0},"how":"plain"}],"poison":1},{"async":false,"deps":[{"src":{"k":"s","i":0},"how":"plain"}],"poison":2}],"tree":{"t":"show","when":{"how":"latest","src":{"k":"m","i":2}},"child":{"t":"group","kids":[{"t":"leaf","text":[{"how":"plain","src":{"k":"m","i":0}}],"attrs":[{"how":"pending","src":{"k":"m","i":0}},{"how":"pending","src":{"k":"s","i":0}}],"grouped":false},{"t":"leaf","text":[{"how":"pending","src":{"k":"m","i":1}}],"attrs":[{"how":"latest","src":{"k":"s","i":0}}],"grouped":false}]},"fallback":true},"steps":[{"t":"task"},{"t":"action","s":0,"v":1},{"t":"write","s":0,"v":2},{"t":"write","s":0,"v":3},{"t":"flush"},{"t":"write","s":0,"v":0},{"t":"task"},{"t":"write","s":0,"v":1},{"t":"write","s":0,"v":3},{"t":"micro"},{"t":"task"},{"t":"micro"}]}

isPending(m1) in a render effect pulls m1; m1's pass calls latest(m0) with m0 held and uninitialized in the flush's own transaction (born into it). Stack: joinPass < verdictValue (the uninitialized-held arm, probing) < read < latest < m1's fn < recompute < pullComputed < verdictValue < read < isPending.

Found by the S1 reader-seat differential (SEAT_SHADOW_REPORT): class Vl4 P/P — world after the read (old 2, new 0; Vl posture; s h u · vseat ahead oldVlane flushTx flushIsElTx), 1 instance at 2k cases × seed 7; none in the signals, solid or web suites. The slot agrees (the staging, P); the pass's world does not. No end-screen difference in this case.

Why it is a bug

  • A31: a memo's pass pulled inside a window is its own; the window answers for what the pass produced. verdictValue applies this to the dispatch (setVerdict(null)) and not to probing/probeFound, so a nested latest() inside the pulled pass re-enters the window with the outer probe's state.
  • F-vworld (2026-10-29): a verdict read leaves marks and does not raise the pass's world; latest() of a born-into-the-future node is a verdict read whichever pass makes it.

Intended fix

The reader-seat rebuild S1 (#4395 → S1 PR) decides the pass's world from its seat and the read's posture (row Vl4), not from a flag the puller left set. Until the swap lands, the shadow carries this as a known class (shadowKnown, this issue). (probeFound's leak is the same bug's other half; the differential compares worlds and slots, not probe answers — the swap should save/clear probing and probeFound around the pulled pass as it does verdict.)

Ngôn ngữ chính
TypeScript
Star
36.2k
Fork
1.1k
Merge trung bình
11 giờ 48 phút
Pull request đã merge (30 ngày)
441

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của solidjs/solid

Tất cả issue của solidjs/solid

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.