-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Specialized ops #7301
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
Specialized ops #7301
Changes from all commits
687e99f
81d307b
0176223
9bb0c46
1c07777
240f3ac
cadb9be
fd098fe
dd29113
b238a27
d950035
32376d5
a7c179c
e1289f1
2350bc1
ba9d528
3c88368
ab6bbb6
48fd5c7
51accdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,9 @@ use crate::{ | |
| object::{Traverse, TraverseFn}, | ||
| }; | ||
| use alloc::fmt; | ||
| use core::{mem::size_of, ops::ControlFlow}; | ||
| use core::mem::size_of; | ||
| use core::ops::ControlFlow; | ||
| use core::sync::atomic::{AtomicU64, Ordering::Relaxed}; | ||
| use num_traits::ToPrimitive; | ||
|
|
||
| // HashIndex is intended to be same size with hash::PyHash | ||
|
|
@@ -34,6 +36,7 @@ type EntryIndex = usize; | |
|
|
||
| pub struct Dict<T = PyObjectRef> { | ||
| inner: PyRwLock<DictInner<T>>, | ||
| version: AtomicU64, | ||
| } | ||
|
|
||
| unsafe impl<T: Traverse> Traverse for Dict<T> { | ||
|
|
@@ -98,6 +101,7 @@ impl<T: Clone> Clone for Dict<T> { | |
| fn clone(&self) -> Self { | ||
| Self { | ||
| inner: PyRwLock::new(self.inner.read().clone()), | ||
| version: AtomicU64::new(0), | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -111,6 +115,7 @@ impl<T> Default for Dict<T> { | |
| indices: vec![IndexEntry::FREE; 8], | ||
| entries: Vec::new(), | ||
| }), | ||
| version: AtomicU64::new(0), | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -254,6 +259,16 @@ impl<T> DictInner<T> { | |
| type PopInnerResult<T> = ControlFlow<Option<DictEntry<T>>>; | ||
|
|
||
| impl<T: Clone> Dict<T> { | ||
| /// Monotonically increasing version counter for mutation tracking. | ||
| pub fn version(&self) -> u64 { | ||
| self.version.load(Relaxed) | ||
| } | ||
|
|
||
| /// Bump the version counter after any mutation. | ||
| fn bump_version(&self) { | ||
| self.version.fetch_add(1, Relaxed); | ||
| } | ||
|
Comment on lines
+263
to
+270
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # 1) Show where version bumps happen relative to lock scopes.
rg -n -C4 'bump_version\(' crates/vm/src/dict_inner.rsRepository: RustPython/RustPython Length of output: 2663 🏁 Script executed: # 2) Show consumers of dict version values (to validate whether reads are lock-free).
rg -n -C4 '\.version\(\)' crates/vm/srcRepository: RustPython/RustPython Length of output: 3008 🏁 Script executed: # 3) Get the full context around lines 263-270 and surrounding code
head -n 300 crates/vm/src/dict_inner.rs | tail -n +250Repository: RustPython/RustPython Length of output: 1941 Use Acquire/Release ordering for version field and perform version bumps under the write lock. The 🔧 Suggested direction-use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
+use core::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}};
...
pub fn version(&self) -> u64 {
- self.version.load(Relaxed)
+ self.version.load(Acquire)
}
...
fn bump_version(&self) {
- self.version.fetch_add(1, Relaxed);
+ self.version.fetch_add(1, Release);
}Also move each 🤖 Prompt for AI Agents |
||
|
|
||
| fn read(&self) -> PyRwLockReadGuard<'_, DictInner<T>> { | ||
| self.inner.read() | ||
| } | ||
|
|
@@ -283,6 +298,7 @@ impl<T: Clone> Dict<T> { | |
| }; | ||
| if entry.index == index_index { | ||
| let removed = core::mem::replace(&mut entry.value, value); | ||
| self.bump_version(); | ||
| // defer dec RC | ||
| break Some(removed); | ||
| } else { | ||
|
|
@@ -298,6 +314,7 @@ impl<T: Clone> Dict<T> { | |
| continue; | ||
| } | ||
| inner.unchecked_push(index_index, hash, key.to_pyobject(vm), value, entry_index); | ||
| self.bump_version(); | ||
| break None; | ||
| } | ||
| }; | ||
|
|
@@ -361,6 +378,7 @@ impl<T: Clone> Dict<T> { | |
| inner.indices.resize(8, IndexEntry::FREE); | ||
| inner.used = 0; | ||
| inner.filled = 0; | ||
| self.bump_version(); | ||
| // defer dec rc | ||
| core::mem::take(&mut inner.entries) | ||
| }; | ||
|
|
@@ -439,6 +457,7 @@ impl<T: Clone> Dict<T> { | |
| continue; | ||
| } | ||
| inner.unchecked_push(index_index, hash, key.to_owned(), value, entry); | ||
| self.bump_version(); | ||
| break None; | ||
| } | ||
| }; | ||
|
|
@@ -475,6 +494,7 @@ impl<T: Clone> Dict<T> { | |
| value.clone(), | ||
| index_entry, | ||
| ); | ||
| self.bump_version(); | ||
| return Ok(value); | ||
| } | ||
| } | ||
|
|
@@ -511,6 +531,7 @@ impl<T: Clone> Dict<T> { | |
| let key_obj = key.to_pyobject(vm); | ||
| let ret = (key_obj.clone(), value.clone()); | ||
| inner.unchecked_push(index_index, hash, key_obj, value, index_entry); | ||
| self.bump_version(); | ||
| return Ok(ret); | ||
| } | ||
| } | ||
|
|
@@ -698,6 +719,7 @@ impl<T: Clone> Dict<T> { | |
| } = IndexEntry::DUMMY; | ||
| inner.used -= 1; | ||
| let removed = slot.take(); | ||
| self.bump_version(); | ||
| Ok(ControlFlow::Break(removed)) | ||
| } | ||
|
|
||
|
|
@@ -727,6 +749,7 @@ impl<T: Clone> Dict<T> { | |
| // entry.index always refers valid index | ||
| inner.indices.get_unchecked_mut(entry.index) | ||
| } = IndexEntry::DUMMY; | ||
| self.bump_version(); | ||
| Some((entry.key, entry.value)) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.