Skip to content

fix: avoid false infinite loop detection when cached signal self-updates#24412

Open
Artur- wants to merge 7 commits into
mainfrom
claude/slack-session-DHaiW
Open

fix: avoid false infinite loop detection when cached signal self-updates#24412
Artur- wants to merge 7 commits into
mainfrom
claude/slack-session-DHaiW

Conversation

@Artur-
Copy link
Copy Markdown
Member

@Artur- Artur- commented May 22, 2026

Summary

  • Add a ThreadLocal flag in Effect to track when we're in a "read-triggered update" context
  • Wrap the cached signal's self-update submit() call in this context
  • Skip infinite loop detection when this flag is set, since it's lazy evaluation rather than an actual write loop

Background

When an effect reads from a cached signal that detects its value is stale (via hasChanges()), the cached signal recomputes its value and submits an update. This update triggers a change notification back to the effect. Previously, this was incorrectly detected as an infinite loop because the effect was still active when the notification arrived.

However, this is not a true infinite loop - it's lazy evaluation happening during a read operation. The effect is reading, not writing, so there's no actual loop.

Test plan

  • Add unit test infiniteLoopDetection_cachedSignalSelfUpdate_notDetectedAsLoop to verify an effect reading a cached signal that self-updates does not throw an infinite loop exception
  • Run existing tests to ensure no regressions in actual infinite loop detection

Fixes #24409

https://claude.ai/code/session_0193Z8ydgjUCCpbgCZwHhGGM


Generated by Claude Code

When an effect reads from a cached signal that detects its value is stale,
the cached signal recomputes and submits an update. This previously
triggered infinite loop detection because the notification happened while
the effect was still active. However, this is lazy evaluation (read-triggered),
not an actual write loop.

Add a ThreadLocal flag to track when we're in a "read-triggered update"
context and skip the infinite loop check in that case.

Fixes #24409

https://claude.ai/code/session_0193Z8ydgjUCCpbgCZwHhGGM
@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented May 22, 2026

CLA assistant check
All committers have signed the CLA.

@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented May 22, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

Test Results

 1 420 files  ±0   1 420 suites  ±0   1h 20m 0s ⏱️ - 1m 59s
10 001 tests +2   9 933 ✅ +2  68 💤 ±0  0 ❌ ±0 
10 473 runs  +2  10 404 ✅ +2  69 💤 ±0  0 ❌ ±0 

Results for commit 4ae364a. ± Comparison against base commit 92cc26c.

♻️ This comment has been updated with latest results.

@Artur- Artur- marked this pull request as ready for review May 25, 2026 06:29
@Artur- Artur- requested a review from Legioth May 25, 2026 06:29
Copy link
Copy Markdown
Member

@Legioth Legioth left a comment

Choose a reason for hiding this comment

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

I'm far from sure, but it feels like this flag is too eager since it also applies to any actual loop that is triggered as a consequence of the cache read. It's only interactions at the boundary between the reader and the inside of the read that should be ignored but now it also ignores anything between effects inside the read.

@Legioth
Copy link
Copy Markdown
Member

Legioth commented May 25, 2026

One alternative could be to stash away the contents of activeEffects and start the cached signal read with a new empty list and then restore the original run once the cached read is completed.

Address review feedback: instead of using a global flag that skips loop
detection for ALL effects during the cached read, stash and restore
activeEffects. This ensures:
- The reading effect won't see itself as active during the cache update
- Effects that run inside the cached read still have proper loop detection

https://claude.ai/code/session_0193Z8ydgjUCCpbgCZwHhGGM
@Artur- Artur- requested a review from Legioth May 25, 2026 07:16
Add a unit test for `Effect.runWithStashedActiveEffects` confirming
that an effect created and self-looping inside the stash is still
caught by infinite-loop detection. This protects against regressing
back to a coarse "skip-all" approach (e.g. a global flag) that would
mask real loops happening inside the cached signal's update cascade.
@sonarqubecloud
Copy link
Copy Markdown

@Legioth Legioth dismissed their stale review May 25, 2026 09:52

Approach seems sensible. I'm removing myself as a reviewer since I don't expect to prioritize having a closer look during the upcoming week.

@Legioth Legioth removed their request for review May 25, 2026 09:52
@mshabarov mshabarov requested a review from platosha May 25, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Signal infinite loop detection is triggered if a cached signal is invalidated by reading it

3 participants