Skip to content

Iterating a cache segfaults on 3.*t while another thread writes to it - #98

Merged
awolverp merged 4 commits into
mainfrom
fix94
Oct 1, 2026
Merged

awolverp merged 4 commits into
mainfrom
fix94

Conversation

@awolverp

@awolverp awolverp commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Fix #94.

Replacing the value of an existing key while iterating was not a problem (except for LRUCache), and this caused a segfault in free-threading versions. From now on, in all free-threading versions, this will raise a RuntimeError in all cache implementations.

(except for LRUCache), and this caused a segfault in free-threaded
versions. From now on, in all free-threaded versions, this will raise a
RuntimeError in all cache implementations.
@awolverp
awolverp merged commit 1c4ec7a into main Oct 1, 2026
12 checks passed
@awolverp
awolverp deleted the fix94 branch October 1, 2026 06:36
awolverp pushed a commit that referenced this pull request Oct 28, 2026
Views (keys/values/items, LFU items_with_frequency, TTL items_with_expire)
hold raw positions into the policy storage and rely on the generation
version to notice changes. They compared the generation without holding
the cache lock and then read, so on free-threaded builds another thread
could free or move entries in between (#94 reported this crash; #98 made
replace() bump the generation, but the check-then-read race remained).

- implement_view_guard! now generates lock_and_check(): take the cache
  lock, then compare the generation, and return the guard so it is held
  while reading. The unlocked check_generation() is removed, so every view
  method goes through the lock (__next__, __len__, and TTL/VTTL __bool__).
- View fields hold the typed cache (Py<PyXCache>) to reach its lock.
- TTL view constructors created the iterator and read the generation under
  two separate locks; a mutation in between made stale positions look
  current. Both now happen under one lock, as VTTL already did.
- docs: iterator methods take the cache lock; calling them from code that
  runs inside an operation on the same cache (a key's __eq__, a value's
  __del__) blocks, like cache[key] there.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Iterating a cache segfaults on 3.14t while another thread writes to it

1 participant