fix: stop iteration from revisiting entries moved by get() - #411
Open
chuanghiduoc wants to merge 1 commit into
Open
chuanghiduoc wants to merge 1 commit into
chuanghiduoc wants to merge 1 commit into
Conversation
Iterating with forEach/keys()/entries()/values() while calling get() on
the visited entry moves it to the MRU end via #moveToTail. The index
walkers follow the re-linked prev/next pointers and land back on nodes
they already yielded, looping c,b,c,b... forever until something crashes
with 'RangeError: Invalid array length'.
Track yielded indexes in the walkers so each entry is visited at most
once per iteration. Deletes during iteration were already safe (the
existing #isValidIndex check); this covers the move case.
Repro:
const c = new LRUCache({ max: 3 })
c.set('a', 1); c.set('b', 2); c.set('c', 3)
for (const k of c.keys()) { c.get(k) }
// loops forever on c/b before this change
Related discussion: isaacs#301
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Iterating with
forEach/keys()/entries()/values()while callingget()on the visited entry moves it to the MRU end via#moveToTail. The index walkers follow the re-linkedprev/nextpointers and land back on nodes they already yielded, so the loop revisits the same entries forever until something throws:Walk-through of the two-entry cycle (
#indexes, walking tail → head via#prev):c(tail), callbackget('c')— no-op, already tail. Next:prev[c] = b.b, callbackget('b')—moveToTail(b)unlinks b from between a and c, relinksa↔c, appends b after c (tail = b). List is nowa ↔ c ↔ b.i = prev[b] = c→ visitscagain, whosegetrestores the old order… and the walk oscillates between steps 2–3 indefinitely.Deletes during iteration were already handled by the
#isValidIndexcheck (the entry's index no longer maps back to its key); this covers the move case.Fix
Track yielded indexes in
#indexes/#rindexesand skip them if the walk circles back. Each entry is now visited at most once per iteration, matching Map iteration semantics.The Set is allocated once per iteration call; for the common case where nothing mutates mid-walk, behavior and results are unchanged.
Tests
test/get-during-iteration.tsasserts that iterating while callingget(k)terminates and visits each key exactly once — fails on master (infinite loop), passes with this change.Full suite:
18430 total / 18364 pass / 66 failon this branch vs18326 / 18258 / 68on unmodified master. The only failing file in both runs is the pre-existing flakytest/tracing.ts; the remaining delta is subtests of my new test file plus run-to-run noise in the same tracing/fetch tests.