GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Use borrowed pointers in TYPE_CACHE instead of strong references by youknowone · Pull Request #7384 · RustPython/RustPython

Conversation

@youknowone

@youknowone youknowone commented Mar 8, 2026 •
edited by coderabbitai Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

The method cache (TYPE_CACHE) was storing strong references (Arc clones) to cached attribute values, which inflated sys.getrefcount(). This caused intermittent test_memoryview failures where refcount assertions would fail depending on GC collection timing.

Store borrowed raw pointers instead. Safety is guaranteed because:

  • type_cache_clear() nullifies all entries during GC collection, before the collector breaks cycles
  • type_cache_clear_version() nullifies entries when a type is modified, before the source dict entry is removed
  • Readers use try_to_owned_from_ptr (safe_inc) to atomically validate and increment the refcount on cache hit

Summary by CodeRabbit

  • Refactor
    • Optimized type cache memory management in the virtual machine for improved efficiency.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@youknowone
Use borrowed pointers in TYPE_CACHE instead of strong references
b7b23e7
The method cache (TYPE_CACHE) was storing strong references (Arc
clones) to cached attribute values, which inflated sys.getrefcount().
This caused intermittent test_memoryview failures where refcount
assertions would fail depending on GC collection timing.

Store borrowed raw pointers instead. Safety is guaranteed because:
- type_cache_clear() nullifies all entries during GC collection,
  before the collector breaks cycles
- type_cache_clear_version() nullifies entries when a type is
  modified, before the source dict entry is removed
- Readers use try_to_owned_from_ptr (safe_inc) to atomically
  validate and increment the refcount on cache hit
@coderabbitai

coderabbitai Bot commented Mar 8, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR converts the type cache from storing owned PyObject references to borrowed pointers, reducing reference counting overhead. The take_value method is replaced with clear_value for cache invalidation. Cache insertion and clearing operations are updated to work with borrowed pointers while maintaining correctness with GC behavior.

Changes

Cohort / File(s) Summary
Type Cache Reference Semantics
crates/vm/src/builtins/type.rs
Changed TypeCacheEntry value field from owned PyObjectRef to borrowed pointer; replaced take_value() with clear_value() for cache invalidation; updated cache clearing and insertion paths to work with borrowed pointers; adjusted PyType.modified to nullify pointers instead of releasing strong references; added safety notes on GC behavior and cycle collection interactions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Add method cache for MRO lookups #7313: Directly related—this PR modifies the MCACHE structures and cache management functions originally introduced in that PR, changing the ownership semantics from owned to borrowed references.

Poem

🐰 Hop, hop! A cache so clever,
From owned refs to borrowed—light as a feather!
No counters to count, no drops to fear,
Just borrowed whispers in memory's ear. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main change: replacing strong references with borrowed pointers in the TYPE_CACHE, which is the core motivation and implementation of this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
  • X
  • Mastodon
  • Reddit
  • LinkedIn

Comment @coderabbitai help to get the list of available commands and usage tips.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@youknowone
youknowone marked this pull request as ready for review March 8, 2026 12:58
coderabbitai[bot]
coderabbitai Bot reviewed Mar 8, 2026
View reviewed changes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/vm/src/builtins/type.rs`:
- Around line 84-89: Update the unsafe lifetime doc for the type cache to cover
both roots: state that entries referencing heap types are cleared by GC via
type_cache_clear() and type_cache_clear_version(), while static
(untracked/immortal) types are not collected and remain valid for the program
lifetime (refer to the static-type untracking logic). Mention both behaviors
explicitly so callers understand that cached pointers may be nulled during GC
for heap types but are permanently valid for static types.
- Around line 805-809: The type dict mutation pathway must always call the
cache-invalidation path before changing attributes; create and use a helper like
mutate_type_dict(&self, f: impl FnOnce(&mut PyAttributes)) that calls
self.modified() then invokes f on self.attributes.write(), and replace all
direct mutations (e.g., calls to self.attributes.write().insert, swap_remove,
remove) in the annotation/cache setters and the sites around the symbols
mentioned (lines handling __annotations_cache__, __annotate_func__, and the
blocks currently using insert/swap_remove) to use this helper so every
post-construction write funnels through the invalidation path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 35c55559-b67c-4e53-ad9e-c8a48f63fa18

📥 Commits

Reviewing files that changed from the base of the PR and between 46abff6 and b7b23e7.

📒 Files selected for processing (1)
  • crates/vm/src/builtins/type.rs

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread crates/vm/src/builtins/type.rs
Comment on lines +84 to +89
/// The cache holds a **borrowed** pointer (no refcount increment).
/// Safety: `type_cache_clear()` nullifies all entries during GC,
/// and `type_cache_clear_version()` nullifies entries when a type
/// is modified — both before the source dict entry is removed.
/// Types are always part of reference cycles (via `mro` self-reference)
/// so they are always collected by the cyclic GC (never refcount-freed).

@coderabbitai coderabbitai Bot Mar 8, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The unsafe lifetime note needs the static-type case too.

Lines 689-696 explicitly untrack static types, so "Types are always collected by the cyclic GC" is not true for every cache owner here. Please document both supported roots explicitly: heap types are cleared by GC, while static types are immortal.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/type.rs` around lines 84 - 89, Update the unsafe
lifetime doc for the type cache to cover both roots: state that entries
referencing heap types are cleared by GC via type_cache_clear() and
type_cache_clear_version(), while static (untracked/immortal) types are not
collected and remain valid for the program lifetime (refer to the static-type
untracking logic). Mention both behaviors explicitly so callers understand that
cached pointers may be nulled during GC for heap types but are permanently valid
for static types.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread crates/vm/src/builtins/type.rs
Comment on lines +805 to 809
// Invalidate caches BEFORE modifying attributes so that borrowed
// pointers in cache entries are nullified while the source objects
// are still alive.
self.modified();
self.attributes.write().insert(attr_name, value);

@coderabbitai coderabbitai Bot Mar 8, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Borrowed-cache safety is still bypassed by other type-dict mutations.

After this switch, ordinary keys like __annotations_cache__ and __annotate_func__ can be cached as borrowed pointers, but at minimum Lines 1379, 1406-1408, 1470-1472, and 1489-1525 still replace/remove those entries with raw self.attributes.write().insert/swap_remove() and no modified() call. A simple t.__annotations_cache__ → mutate annotations → t.__annotations_cache__ sequence can leave the cache pointing at freed storage. Please funnel every post-construction attributes mutation through the same invalidation path.

Suggested direction
fn mutate_type_dict(&self, f: impl FnOnce(&mut PyAttributes)) {
    self.modified();
    f(&mut self.attributes.write());
}

Use that helper for the annotation/cache setters and any other runtime self.attributes writes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/type.rs` around lines 805 - 809, The type dict
mutation pathway must always call the cache-invalidation path before changing
attributes; create and use a helper like mutate_type_dict(&self, f: impl
FnOnce(&mut PyAttributes)) that calls self.modified() then invokes f on
self.attributes.write(), and replace all direct mutations (e.g., calls to
self.attributes.write().insert, swap_remove, remove) in the annotation/cache
setters and the sites around the symbols mentioned (lines handling
__annotations_cache__, __annotate_func__, and the blocks currently using
insert/swap_remove) to use this helper so every post-construction write funnels
through the invalidation path.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@youknowone
youknowone merged commit ce8952b into RustPython:main Mar 8, 2026
12 of 13 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@youknowone
youknowone deleted the type-cache branch March 8, 2026 13:55
youknowone added a commit to youknowone/RustPython that referenced this pull request Mar 22, 2026
@youknowone
Use borrowed pointers in TYPE_CACHE instead of strong references (Rus…
595dde1
…tPython#7384)

The method cache (TYPE_CACHE) was storing strong references (Arc
clones) to cached attribute values, which inflated sys.getrefcount().
This caused intermittent test_memoryview failures where refcount
assertions would fail depending on GC collection timing.

Store borrowed raw pointers instead. Safety is guaranteed because:
- type_cache_clear() nullifies all entries during GC collection,
  before the collector breaks cycles
- type_cache_clear_version() nullifies entries when a type is
  modified, before the source dict entry is removed
- Readers use try_to_owned_from_ptr (safe_inc) to atomically
  validate and increment the refcount on cache hit
@coderabbitai coderabbitai Bot mentioned this pull request Mar 27, 2026
Thread-safe type operations: type lock, QSBR type-cache reclamation, GC stop-the-world, and interpreter optimizations #7416
Merged
youknowone added a commit that referenced this pull request Sep 16, 2026
@youknowone
Use borrowed pointers in TYPE_CACHE instead of strong references (#7384)
fc8f93f
The method cache (TYPE_CACHE) was storing strong references (Arc
clones) to cached attribute values, which inflated sys.getrefcount().
This caused intermittent test_memoryview failures where refcount
assertions would fail depending on GC collection timing.

Store borrowed raw pointers instead. Safety is guaranteed because:
- type_cache_clear() nullifies all entries during GC collection,
  before the collector breaks cycles
- type_cache_clear_version() nullifies entries when a type is
  modified, before the source dict entry is removed
- Readers use try_to_owned_from_ptr (safe_inc) to atomically
  validate and increment the refcount on cache hit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@coderabbitai coderabbitai[bot] coderabbitai[bot] left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@youknowone
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.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.