Skip to content

Reading from an LRUCache while iterating it crashes the process #75

Description

@Malkiz223

On LRUCache a read promotes the entry: in, get(), cache[key] and setdefault() move it to the back of the internal list. Doing that while an iterator is alive leaves the iterator on a relinked list, and the process dies with an access violation.

import cachebox

cache = cachebox.LRUCache(10)
for key in ('a', 'b', 'c'):
    cache[key] = 1

it = cache.keys()
print('promoting "a" with a lookup ...', flush=True)
assert 'a' in cache

print('walking the live iterator ...', flush=True)
print('got:', list(it))

What list(it) gives after that promotion, by version:

6.2.1 and 6.2.2 6.2.3 and 6.2.4 with the fix
list(it) got: ['a'], two of the three keys silently vanish the process dies with an access violation RuntimeError, same as after an insert

cache.get('a'), cache['a'] and cache.setdefault('a') behave the same as 'a' in cache. Checked on Windows, CPython 3.12.

The missing bump is old, only its price changed. On 6.2.1 and 6.2.2 the iterator follows the promoted entry to the back of the list and ends right after it, so keys quietly vanish. The linked list rework from #66 made the same walk die instead, which is actually a step forward: a loud failure gets noticed, while the quiet one had been eating keys unnoticed all along.

An insert under a live iterator raises RuntimeError. A promoting read rewires the same list, it just does not bump the generation, so the iterator never notices.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions