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

fix(core): Fix possible infinite loop with markForCheck by partially reverting #54074 #54329

Closed

Conversation

atscott
Copy link
Contributor

@atscott atscott commented Feb 8, 2024

In some situations, calling markForCheck can result in an infinite loop in seemingly valid scenarios. When a transplanted view is inserted before its declaration, it gets refreshed in the retry loop of detectChanges. At this point, the Dirty flag has been cleared from all parents. Calling markForCheck marks the insertion tree up to the root Dirty. If the declaration is checked again as a result (i.e. because it has default change detection) and is reachable because its parent was marked Dirty, this can cause an infinite loop. The declaration is refreshed again, so the insertion is marked for refresh (again). We enter an infinite loop if the insertion tree always calls markForCheck for some reason (i.e. {{createReplayObservable() | async}}).

While the case above does fall into an infinite loop, it also truly is a problem in the application. While it's not an infinite synchronous loop, the declaration and insertion are infinitely dirty and will be refreshed on every change detection round.

Usually markForCheck does not have this problem because the Dirty flag is not cleared until the very end of change detection. However, if the view did not already have the Dirty flag set, it is never cleared because we never entered view refresh. One solution to this problem could be to clear the Dirty flag even after skipping view refresh but traversing to children.

…y reverting angular#54074

In some situations, calling `markForCheck` can result in an infinite
loop in seemingly valid scenarios. When a transplanted view is inserted
before its declaration, it gets refreshed in the retry loop of
`detectChanges`. At this point, the `Dirty` flag has been cleared from
all parents. Calling `markForCheck` marks the insertion tree up to the
root `Dirty`. If the declaration is checked again as a result (i.e.
because it has default change detection) and is reachable because its
parent was marked `Dirty`, this can cause an infinite loop. The
declaration is refreshed again, so the insertion is marked for refresh
(again). We enter an infinite loop if the insertion tree always calls
`markForCheck` for some reason (i.e. `{{createReplayObservable() | async}}`).

While the case above does fall into an infinite loop, it also truly is a
problem in the application. While it's not an infinite synchronous loop,
the declaration and insertion are infinitely dirty and will be refreshed
on every change detection round.

Usually `markForCheck` does not have this problem because the `Dirty`
flag is not cleared until the very end of change detection. However, if
the view did not already have the `Dirty` flag set, it is never cleared
because we never entered view refresh. One solution to this problem
could be to clear the `Dirty` flag even after skipping view refresh but
traversing to children.
@atscott atscott force-pushed the transplantedViewsAreProblematic branch from c3f2ece to 3b27313 Compare February 8, 2024 00:49
@atscott atscott added area: core Issues related to the framework runtime target: patch This PR is targeted for the next patch release labels Feb 8, 2024
@ngbot ngbot bot added this to the Backlog milestone Feb 8, 2024
@atscott atscott added the regression Indicates than the issue relates to something that worked in a previous version label Feb 8, 2024
Copy link
Contributor

@jessicajaniuk jessicajaniuk left a comment

Choose a reason for hiding this comment

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

LGTM

@jessicajaniuk jessicajaniuk added the action: merge The PR is ready for merge by the caretaker label Feb 8, 2024
jessicajaniuk pushed a commit that referenced this pull request Feb 8, 2024
…y reverting #54074 (#54329)

In some situations, calling `markForCheck` can result in an infinite
loop in seemingly valid scenarios. When a transplanted view is inserted
before its declaration, it gets refreshed in the retry loop of
`detectChanges`. At this point, the `Dirty` flag has been cleared from
all parents. Calling `markForCheck` marks the insertion tree up to the
root `Dirty`. If the declaration is checked again as a result (i.e.
because it has default change detection) and is reachable because its
parent was marked `Dirty`, this can cause an infinite loop. The
declaration is refreshed again, so the insertion is marked for refresh
(again). We enter an infinite loop if the insertion tree always calls
`markForCheck` for some reason (i.e. `{{createReplayObservable() | async}}`).

While the case above does fall into an infinite loop, it also truly is a
problem in the application. While it's not an infinite synchronous loop,
the declaration and insertion are infinitely dirty and will be refreshed
on every change detection round.

Usually `markForCheck` does not have this problem because the `Dirty`
flag is not cleared until the very end of change detection. However, if
the view did not already have the `Dirty` flag set, it is never cleared
because we never entered view refresh. One solution to this problem
could be to clear the `Dirty` flag even after skipping view refresh but
traversing to children.

PR Close #54329
@jessicajaniuk
Copy link
Contributor

This PR was merged into the repository by commit 898a532.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime regression Indicates than the issue relates to something that worked in a previous version target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants