mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
type lock
This commit is contained in:
1 parent
38de7462c0
commit
ea2d66e799
8 files changed
+306
-184
No files matched your search
+278
-144
@@ -451,9 +451,15 @@ fn is_subtype_with_mro(a_mro: &[PyTypeRef], a: &Py<PyType>, b: &Py<PyType>) -> b
|
||||
}
|
||||
|
||||
impl PyType {
|
||||
#[inline]
|
||||
fn with_type_lock<R>(vm: &VirtualMachine, f: impl FnOnce() -> R) -> R {
|
||||
let _guard = vm.state.type_mutex.lock();
|
||||
f()
|
||||
}
|
||||
|
||||
/// Assign a fresh version tag. Returns 0 if the version counter has been
|
||||
/// exhausted, in which case no new cache entries can be created.
|
||||
pub fn assign_version_tag(&self) -> u32 {
|
||||
fn assign_version_tag_inner(&self) -> u32 {
|
||||
let v = self.tp_version_tag.load(Ordering::Acquire);
|
||||
if v != 0 {
|
||||
return v;
|
||||
@@ -461,7 +467,7 @@ impl PyType {
|
||||
|
||||
// Assign versions to all direct bases first (MRO invariant).
|
||||
for base in self.bases.read().iter() {
|
||||
if base.assign_version_tag() == 0 {
|
||||
if base.assign_version_tag_inner() == 0 {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -481,8 +487,23 @@ impl PyType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_version_tag(&self) -> u32 {
|
||||
self.assign_version_tag_inner()
|
||||
}
|
||||
|
||||
pub(crate) fn version_for_specialization(&self, vm: &VirtualMachine) -> u32 {
|
||||
Self::with_type_lock(vm, || {
|
||||
let version = self.tp_version_tag.load(Ordering::Acquire);
|
||||
if version == 0 {
|
||||
self.assign_version_tag_inner()
|
||||
} else {
|
||||
version
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Invalidate this type's version tag and cascade to all subclasses.
|
||||
pub fn modified(&self) {
|
||||
fn modified_inner(&self) {
|
||||
if let Some(ext) = self.heaptype_ext.as_ref() {
|
||||
ext.specialization_cache.invalidate_for_type_modified();
|
||||
}
|
||||
@@ -499,11 +520,15 @@ impl PyType {
|
||||
let subclasses = self.subclasses.read();
|
||||
for weak_ref in subclasses.iter() {
|
||||
if let Some(sub) = weak_ref.upgrade() {
|
||||
sub.downcast_ref::<PyType>().unwrap().modified();
|
||||
sub.downcast_ref::<PyType>().unwrap().modified_inner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modified(&self) {
|
||||
self.modified_inner();
|
||||
}
|
||||
|
||||
pub fn new_simple_heap(
|
||||
name: &str,
|
||||
base: &Py<PyType>,
|
||||
@@ -892,6 +917,74 @@ impl PyType {
|
||||
self.find_name_in_mro(attr_name)
|
||||
}
|
||||
|
||||
/// CPython-style `_PyType_LookupRefAndVersion` equivalent for interned names.
|
||||
/// Returns the observed lookup result and the type version used for the lookup.
|
||||
pub(crate) fn lookup_ref_and_version_interned(
|
||||
&self,
|
||||
name: &'static PyStrInterned,
|
||||
vm: &VirtualMachine,
|
||||
) -> (Option<PyObjectRef>, u32) {
|
||||
let version = self.tp_version_tag.load(Ordering::Acquire);
|
||||
if version != 0 {
|
||||
let idx = type_cache_hash(version, name);
|
||||
let entry = &TYPE_CACHE[idx];
|
||||
let name_ptr = name as *const _ as *mut _;
|
||||
loop {
|
||||
let seq1 = entry.begin_read();
|
||||
let entry_version = entry.version.load(Ordering::Acquire);
|
||||
let type_version = self.tp_version_tag.load(Ordering::Acquire);
|
||||
if entry_version != type_version
|
||||
|| !core::ptr::eq(entry.name.load(Ordering::Relaxed), name_ptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
let ptr = entry.value.load(Ordering::Acquire);
|
||||
if ptr.is_null() {
|
||||
if entry.end_read(seq1) {
|
||||
return (None, entry_version);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(cloned) = unsafe { PyObject::try_to_owned_from_ptr(ptr) } {
|
||||
let same_ptr = core::ptr::eq(entry.value.load(Ordering::Relaxed), ptr);
|
||||
if same_ptr && entry.end_read(seq1) {
|
||||
return (Some(cloned), entry_version);
|
||||
}
|
||||
drop(cloned);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Self::with_type_lock(vm, || {
|
||||
let assigned = if self.tp_version_tag.load(Ordering::Acquire) == 0 {
|
||||
self.assign_version_tag_inner()
|
||||
} else {
|
||||
self.tp_version_tag.load(Ordering::Acquire)
|
||||
};
|
||||
let result = self.find_name_in_mro_uncached(name);
|
||||
if assigned != 0
|
||||
&& !TYPE_CACHE_CLEARING.load(Ordering::Acquire)
|
||||
&& self.tp_version_tag.load(Ordering::Acquire) == assigned
|
||||
{
|
||||
let idx = type_cache_hash(assigned, name);
|
||||
let entry = &TYPE_CACHE[idx];
|
||||
let name_ptr = name as *const _ as *mut _;
|
||||
entry.begin_write();
|
||||
entry.version.store(0, Ordering::Release);
|
||||
let new_ptr = result.as_ref().map_or(core::ptr::null_mut(), |found| {
|
||||
&**found as *const PyObject as *mut _
|
||||
});
|
||||
entry.value.store(new_ptr, Ordering::Relaxed);
|
||||
entry.name.store(name_ptr, Ordering::Relaxed);
|
||||
entry.version.store(assigned, Ordering::Release);
|
||||
entry.end_write();
|
||||
}
|
||||
(result, assigned)
|
||||
})
|
||||
}
|
||||
|
||||
/// Cache __init__ for CALL_ALLOC_AND_ENTER_INIT specialization.
|
||||
/// The cache is valid only when guarded by the type version check.
|
||||
pub(crate) fn cache_init_for_specialization(
|
||||
@@ -906,15 +999,17 @@ impl PyType {
|
||||
if tp_version == 0 {
|
||||
return false;
|
||||
}
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
let _guard = ext.specialization_cache.write_lock.lock();
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
ext.specialization_cache.swap_init(Some(init), Some(vm));
|
||||
true
|
||||
Self::with_type_lock(vm, || {
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
let _guard = ext.specialization_cache.write_lock.lock();
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
ext.specialization_cache.swap_init(Some(init), Some(vm));
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
/// Read cached __init__ for CALL_ALLOC_AND_ENTER_INIT specialization.
|
||||
@@ -948,20 +1043,22 @@ impl PyType {
|
||||
if tp_version == 0 {
|
||||
return false;
|
||||
}
|
||||
let _guard = ext.specialization_cache.write_lock.lock();
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
let func_version = getitem.get_version_for_current_state();
|
||||
if func_version == 0 {
|
||||
return false;
|
||||
}
|
||||
ext.specialization_cache
|
||||
.getitem_version
|
||||
.store(func_version, Ordering::Release);
|
||||
ext.specialization_cache
|
||||
.swap_getitem(Some(getitem), Some(vm));
|
||||
true
|
||||
Self::with_type_lock(vm, || {
|
||||
let _guard = ext.specialization_cache.write_lock.lock();
|
||||
if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
|
||||
return false;
|
||||
}
|
||||
let func_version = getitem.get_version_for_current_state();
|
||||
if func_version == 0 {
|
||||
return false;
|
||||
}
|
||||
ext.specialization_cache
|
||||
.getitem_version
|
||||
.store(func_version, Ordering::Release);
|
||||
ext.specialization_cache
|
||||
.swap_getitem(Some(getitem), Some(vm));
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
/// Read cached __getitem__ for BINARY_OP_SUBSCR_GETITEM specialization.
|
||||
@@ -1327,38 +1424,41 @@ impl PyType {
|
||||
// // TODO: how to uniquely identify the subclasses to remove?
|
||||
// }
|
||||
|
||||
*zelf.bases.write() = bases;
|
||||
// Recursively update the mros of this class and all subclasses
|
||||
fn update_mro_recursively(cls: &PyType, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut mro =
|
||||
PyType::resolve_mro(&cls.bases.read()).map_err(|msg| vm.new_type_error(msg))?;
|
||||
// Preserve self (mro[0]) when updating MRO
|
||||
mro.insert(0, cls.mro.read()[0].to_owned());
|
||||
*cls.mro.write() = mro;
|
||||
for subclass in cls.subclasses.write().iter() {
|
||||
let subclass = subclass.upgrade().unwrap();
|
||||
let subclass: &Py<PyType> = subclass.downcast_ref().unwrap();
|
||||
update_mro_recursively(subclass, vm)?;
|
||||
Self::with_type_lock(vm, || {
|
||||
*zelf.bases.write() = bases;
|
||||
// Recursively update the mros of this class and all subclasses
|
||||
fn update_mro_recursively(cls: &PyType, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut mro =
|
||||
PyType::resolve_mro(&cls.bases.read()).map_err(|msg| vm.new_type_error(msg))?;
|
||||
// Preserve self (mro[0]) when updating MRO
|
||||
mro.insert(0, cls.mro.read()[0].to_owned());
|
||||
*cls.mro.write() = mro;
|
||||
for subclass in cls.subclasses.write().iter() {
|
||||
let subclass = subclass.upgrade().unwrap();
|
||||
let subclass: &Py<PyType> = subclass.downcast_ref().unwrap();
|
||||
update_mro_recursively(subclass, vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
update_mro_recursively(zelf, vm)?;
|
||||
|
||||
// Invalidate inline caches
|
||||
zelf.modified_inner();
|
||||
|
||||
// TODO: do any old slots need to be cleaned up first?
|
||||
zelf.init_slots(&vm.ctx);
|
||||
|
||||
// Register this type as a subclass of its new bases
|
||||
let weakref_type = super::PyWeak::static_type();
|
||||
for base in zelf.bases.read().iter() {
|
||||
base.subclasses.write().push(
|
||||
zelf.as_object()
|
||||
.downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
update_mro_recursively(zelf, vm)?;
|
||||
|
||||
// Invalidate inline caches
|
||||
zelf.modified();
|
||||
|
||||
// TODO: do any old slots need to be cleaned up first?
|
||||
zelf.init_slots(&vm.ctx);
|
||||
|
||||
// Register this type as a subclass of its new bases
|
||||
let weakref_type = super::PyWeak::static_type();
|
||||
for base in zelf.bases.read().iter() {
|
||||
base.subclasses.write().push(
|
||||
zelf.as_object()
|
||||
.downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1450,20 +1550,30 @@ impl PyType {
|
||||
)));
|
||||
}
|
||||
|
||||
let mut attrs = self.attributes.write();
|
||||
// First try __annotate__, in case that's been set explicitly
|
||||
if let Some(annotate) = attrs.get(identifier!(vm, __annotate__)).cloned() {
|
||||
let annotate_key = identifier!(vm, __annotate__);
|
||||
let annotate_func_key = identifier!(vm, __annotate_func__);
|
||||
let attrs = self.attributes.read();
|
||||
if let Some(annotate) = attrs.get(annotate_key).cloned() {
|
||||
return Ok(annotate);
|
||||
}
|
||||
// Then try __annotate_func__
|
||||
if let Some(annotate) = attrs.get(identifier!(vm, __annotate_func__)).cloned() {
|
||||
// TODO: Apply descriptor tp_descr_get if needed
|
||||
if let Some(annotate) = attrs.get(annotate_func_key).cloned() {
|
||||
return Ok(annotate);
|
||||
}
|
||||
// Set __annotate_func__ = None and return None
|
||||
drop(attrs);
|
||||
|
||||
let none = vm.ctx.none();
|
||||
attrs.insert(identifier!(vm, __annotate_func__), none.clone());
|
||||
Ok(none)
|
||||
Ok(Self::with_type_lock(vm, || {
|
||||
let mut attrs = self.attributes.write();
|
||||
if let Some(annotate) = attrs.get(annotate_key).cloned() {
|
||||
return annotate;
|
||||
}
|
||||
if let Some(annotate) = attrs.get(annotate_func_key).cloned() {
|
||||
return annotate;
|
||||
}
|
||||
self.modified_inner();
|
||||
attrs.insert(annotate_func_key, none.clone());
|
||||
none
|
||||
}))
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
@@ -1486,20 +1596,24 @@ impl PyType {
|
||||
return Err(vm.new_type_error("__annotate__ must be callable or None"));
|
||||
}
|
||||
|
||||
let mut attrs = self.attributes.write();
|
||||
// Clear cached annotations only when setting to a new callable
|
||||
if !vm.is_none(&value) {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
}
|
||||
attrs.insert(identifier!(vm, __annotate_func__), value.clone());
|
||||
Self::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
let mut attrs = self.attributes.write();
|
||||
if !vm.is_none(&value) {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
}
|
||||
attrs.insert(identifier!(vm, __annotate_func__), value);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
let annotations_key = identifier!(vm, __annotations__);
|
||||
let annotations_cache_key = identifier!(vm, __annotations_cache__);
|
||||
let attrs = self.attributes.read();
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations__)).cloned() {
|
||||
if let Some(annotations) = attrs.get(annotations_key).cloned() {
|
||||
// Ignore the __annotations__ descriptor stored on type itself.
|
||||
if !annotations.class().is(vm.ctx.types.getset_type) {
|
||||
if vm.is_none(&annotations)
|
||||
@@ -1514,8 +1628,7 @@ impl PyType {
|
||||
)));
|
||||
}
|
||||
}
|
||||
// Then try __annotations_cache__
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations_cache__)).cloned() {
|
||||
if let Some(annotations) = attrs.get(annotations_cache_key).cloned() {
|
||||
if vm.is_none(&annotations)
|
||||
|| annotations.class().is(vm.ctx.types.dict_type)
|
||||
|| self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
@@ -1552,11 +1665,20 @@ impl PyType {
|
||||
vm.ctx.new_dict().into()
|
||||
};
|
||||
|
||||
// Cache the result in __annotations_cache__
|
||||
self.attributes
|
||||
.write()
|
||||
.insert(identifier!(vm, __annotations_cache__), annotations.clone());
|
||||
Ok(annotations)
|
||||
Ok(Self::with_type_lock(vm, || {
|
||||
let mut attrs = self.attributes.write();
|
||||
if let Some(existing) = attrs.get(annotations_key).cloned()
|
||||
&& !existing.class().is(vm.ctx.types.getset_type)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
if let Some(existing) = attrs.get(annotations_cache_key).cloned() {
|
||||
return existing;
|
||||
}
|
||||
self.modified_inner();
|
||||
attrs.insert(annotations_cache_key, annotations.clone());
|
||||
annotations
|
||||
}))
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
@@ -1572,43 +1694,45 @@ impl PyType {
|
||||
)));
|
||||
}
|
||||
|
||||
let mut attrs = self.attributes.write();
|
||||
let has_annotations = attrs.contains_key(identifier!(vm, __annotations__));
|
||||
Self::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
let mut attrs = self.attributes.write();
|
||||
let has_annotations = attrs.contains_key(identifier!(vm, __annotations__));
|
||||
|
||||
match value {
|
||||
crate::function::PySetterValue::Assign(value) => {
|
||||
// SET path: store the value (including None)
|
||||
let key = if has_annotations {
|
||||
identifier!(vm, __annotations__)
|
||||
} else {
|
||||
identifier!(vm, __annotations_cache__)
|
||||
};
|
||||
attrs.insert(key, value);
|
||||
if has_annotations {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
match value {
|
||||
crate::function::PySetterValue::Assign(value) => {
|
||||
let key = if has_annotations {
|
||||
identifier!(vm, __annotations__)
|
||||
} else {
|
||||
identifier!(vm, __annotations_cache__)
|
||||
};
|
||||
attrs.insert(key, value);
|
||||
if has_annotations {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
}
|
||||
}
|
||||
crate::function::PySetterValue::Delete => {
|
||||
let removed = if has_annotations {
|
||||
attrs
|
||||
.swap_remove(identifier!(vm, __annotations__))
|
||||
.is_some()
|
||||
} else {
|
||||
attrs
|
||||
.swap_remove(identifier!(vm, __annotations_cache__))
|
||||
.is_some()
|
||||
};
|
||||
if !removed {
|
||||
return Err(vm.new_attribute_error("__annotations__"));
|
||||
}
|
||||
if has_annotations {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::function::PySetterValue::Delete => {
|
||||
// DELETE path: remove the key
|
||||
let removed = if has_annotations {
|
||||
attrs
|
||||
.swap_remove(identifier!(vm, __annotations__))
|
||||
.is_some()
|
||||
} else {
|
||||
attrs
|
||||
.swap_remove(identifier!(vm, __annotations_cache__))
|
||||
.is_some()
|
||||
};
|
||||
if !removed {
|
||||
return Err(vm.new_attribute_error("__annotations__"));
|
||||
}
|
||||
if has_annotations {
|
||||
attrs.swap_remove(identifier!(vm, __annotations_cache__));
|
||||
}
|
||||
}
|
||||
}
|
||||
attrs.swap_remove(identifier!(vm, __annotate_func__));
|
||||
attrs.swap_remove(identifier!(vm, __annotate__));
|
||||
attrs.swap_remove(identifier!(vm, __annotate_func__));
|
||||
attrs.swap_remove(identifier!(vm, __annotate__));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1641,9 +1765,12 @@ impl PyType {
|
||||
#[pygetset(setter)]
|
||||
fn set___module__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
self.check_set_special_type_attr(identifier!(vm, __module__), vm)?;
|
||||
let mut attributes = self.attributes.write();
|
||||
attributes.swap_remove(identifier!(vm, __firstlineno__));
|
||||
attributes.insert(identifier!(vm, __module__), value);
|
||||
Self::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
self.attributes
|
||||
.write()
|
||||
.insert(identifier!(vm, __module__), value);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1765,24 +1892,26 @@ impl PyType {
|
||||
value: PySetterValue<PyTupleRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let key = identifier!(vm, __type_params__);
|
||||
match value {
|
||||
PySetterValue::Assign(ref val) => {
|
||||
let key = identifier!(vm, __type_params__);
|
||||
PySetterValue::Assign(val) => {
|
||||
self.check_set_special_type_attr(key, vm)?;
|
||||
self.modified();
|
||||
self.attributes.write().insert(key, val.clone().into());
|
||||
Self::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
self.attributes.write().insert(key, val.into());
|
||||
});
|
||||
}
|
||||
PySetterValue::Delete => {
|
||||
// For delete, we still need to check if the type is immutable
|
||||
if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"cannot delete '__type_params__' attribute of immutable type '{}'",
|
||||
self.slot_name()
|
||||
)));
|
||||
}
|
||||
let key = identifier!(vm, __type_params__);
|
||||
self.modified();
|
||||
self.attributes.write().shift_remove(&key);
|
||||
Self::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
self.attributes.write().shift_remove(&key);
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -2390,10 +2519,12 @@ impl Py<PyType> {
|
||||
// Check if we can set this special type attribute
|
||||
self.check_set_special_type_attr(identifier!(vm, __doc__), vm)?;
|
||||
|
||||
// Set the __doc__ in the type's dict
|
||||
self.attributes
|
||||
.write()
|
||||
.insert(identifier!(vm, __doc__), value);
|
||||
PyType::with_type_lock(vm, || {
|
||||
self.modified_inner();
|
||||
self.attributes
|
||||
.write()
|
||||
.insert(identifier!(vm, __doc__), value);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2455,23 +2586,26 @@ impl SetAttr for PyType {
|
||||
}
|
||||
let assign = value.is_assign();
|
||||
|
||||
// Invalidate inline caches before modifying attributes.
|
||||
// This ensures other threads see the version invalidation before
|
||||
// any attribute changes, preventing use-after-free of cached descriptors.
|
||||
zelf.modified();
|
||||
Self::with_type_lock(vm, || {
|
||||
// Invalidate inline caches before modifying attributes.
|
||||
// This ensures other threads see the version invalidation before
|
||||
// any attribute changes, preventing use-after-free of cached descriptors.
|
||||
zelf.modified_inner();
|
||||
|
||||
if let PySetterValue::Assign(value) = value {
|
||||
zelf.attributes.write().insert(attr_name, value);
|
||||
} else {
|
||||
let prev_value = zelf.attributes.write().shift_remove(attr_name); // TODO: swap_remove applicable?
|
||||
if prev_value.is_none() {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
zelf.name(),
|
||||
attr_name,
|
||||
)));
|
||||
if let PySetterValue::Assign(value) = value {
|
||||
zelf.attributes.write().insert(attr_name, value);
|
||||
} else {
|
||||
let prev_value = zelf.attributes.write().shift_remove(attr_name); // TODO: swap_remove applicable?
|
||||
if prev_value.is_none() {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
zelf.name(),
|
||||
attr_name,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
|
||||
if assign {
|
||||
|
||||
+11
-34
@@ -7316,10 +7316,7 @@ impl ExecutingFrame<'_> {
|
||||
.load()
|
||||
.is_some_and(|f| f as usize == PyBaseObject::getattro as *const () as usize);
|
||||
if !is_default_getattro {
|
||||
let mut type_version = cls.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = cls.assign_version_tag();
|
||||
}
|
||||
let type_version = cls.version_for_specialization(_vm);
|
||||
if type_version != 0
|
||||
&& !oparg.is_method()
|
||||
&& !self.specialization_eval_frame_active(_vm)
|
||||
@@ -7357,10 +7354,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
// Get or assign type version
|
||||
let mut type_version = cls.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = cls.assign_version_tag();
|
||||
}
|
||||
let type_version = cls.version_for_specialization(_vm);
|
||||
if type_version == 0 {
|
||||
// Version counter overflow — backoff to avoid re-attempting every execution
|
||||
unsafe {
|
||||
@@ -7580,10 +7574,7 @@ impl ExecutingFrame<'_> {
|
||||
let owner_type = obj.downcast_ref::<PyType>().unwrap();
|
||||
|
||||
// Get or assign type version for the type object itself
|
||||
let mut type_version = owner_type.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = owner_type.assign_version_tag();
|
||||
}
|
||||
let type_version = owner_type.version_for_specialization(_vm);
|
||||
if type_version == 0 {
|
||||
unsafe {
|
||||
self.code.instructions.write_adaptive_counter(
|
||||
@@ -7618,10 +7609,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
let mut metaclass_version = 0;
|
||||
if !mcl.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
metaclass_version = mcl.tp_version_tag.load(Acquire);
|
||||
if metaclass_version == 0 {
|
||||
metaclass_version = mcl.assign_version_tag();
|
||||
}
|
||||
metaclass_version = mcl.version_for_specialization(_vm);
|
||||
if metaclass_version == 0 {
|
||||
unsafe {
|
||||
self.code.instructions.write_adaptive_counter(
|
||||
@@ -7808,16 +7796,14 @@ impl ExecutingFrame<'_> {
|
||||
Some(Instruction::BinaryOpSubscrListSlice)
|
||||
} else {
|
||||
let cls = a.class();
|
||||
let (getitem, type_version) =
|
||||
cls.lookup_ref_and_version_interned(identifier!(vm, __getitem__), vm);
|
||||
if cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
&& !self.specialization_eval_frame_active(vm)
|
||||
&& let Some(_getitem) = cls.get_attr(identifier!(vm, __getitem__))
|
||||
&& let Some(_getitem) = getitem
|
||||
&& let Some(func) = _getitem.downcast_ref_if_exact::<PyFunction>(vm)
|
||||
&& func.can_specialize_call(2)
|
||||
{
|
||||
let mut type_version = cls.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = cls.assign_version_tag();
|
||||
}
|
||||
if type_version != 0 {
|
||||
if cls.cache_getitem_for_specialization(
|
||||
func.to_owned(),
|
||||
@@ -8356,11 +8342,8 @@ impl ExecutingFrame<'_> {
|
||||
&& cls_new_fn as usize == obj_new_fn as usize
|
||||
&& cls_alloc_fn as usize == obj_alloc_fn as usize
|
||||
{
|
||||
let init = cls.get_attr(identifier!(vm, __init__));
|
||||
let mut version = cls.tp_version_tag.load(Acquire);
|
||||
if version == 0 {
|
||||
version = cls.assign_version_tag();
|
||||
}
|
||||
let (init, version) =
|
||||
cls.lookup_ref_and_version_interned(identifier!(vm, __init__), vm);
|
||||
if version == 0 {
|
||||
unsafe {
|
||||
self.code.instructions.write_adaptive_counter(
|
||||
@@ -8680,10 +8663,7 @@ impl ExecutingFrame<'_> {
|
||||
&& cls.slots.as_sequence.length.load().is_none()
|
||||
{
|
||||
// Cache type version for ToBoolAlwaysTrue guard
|
||||
let mut type_version = cls.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = cls.assign_version_tag();
|
||||
}
|
||||
let type_version = cls.version_for_specialization(vm);
|
||||
if type_version != 0 {
|
||||
unsafe {
|
||||
self.code
|
||||
@@ -9021,10 +9001,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
|
||||
// Get or assign type version
|
||||
let mut type_version = cls.tp_version_tag.load(Acquire);
|
||||
if type_version == 0 {
|
||||
type_version = cls.assign_version_tag();
|
||||
}
|
||||
let type_version = cls.version_for_specialization(vm);
|
||||
if type_version == 0 {
|
||||
unsafe {
|
||||
self.code.instructions.write_adaptive_counter(
|
||||
|
||||
@@ -2554,10 +2554,11 @@ fn make_fields(
|
||||
}
|
||||
|
||||
let new_descr = super::PyCField::new_from_field(fdescr, index, offset);
|
||||
cls.set_attr(
|
||||
cls.as_object().set_attr(
|
||||
vm.ctx.intern_str(fname.as_wtf8()),
|
||||
new_descr.to_pyobject(vm),
|
||||
);
|
||||
vm,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -2596,10 +2597,11 @@ pub(super) fn make_anon_fields(cls: &Py<PyType>, vm: &VirtualMachine) -> PyResul
|
||||
|
||||
let mut new_descr = super::PyCField::new_from_field(descr, 0, 0);
|
||||
new_descr.set_anonymous(true);
|
||||
cls.set_attr(
|
||||
cls.as_object().set_attr(
|
||||
vm.ctx.intern_str(fname.as_wtf8()),
|
||||
new_descr.to_pyobject(vm),
|
||||
);
|
||||
vm,
|
||||
)?;
|
||||
|
||||
make_fields(cls, descr, descr.index, descr.offset, vm)?;
|
||||
}
|
||||
|
||||
@@ -498,7 +498,11 @@ impl PyCStructType {
|
||||
};
|
||||
|
||||
// Set the CField as a class attribute
|
||||
cls.set_attr(vm.ctx.intern_str(name.clone()), c_field.to_pyobject(vm));
|
||||
cls.as_object().set_attr(
|
||||
vm.ctx.intern_str(name.clone()),
|
||||
c_field.to_pyobject(vm),
|
||||
vm,
|
||||
)?;
|
||||
|
||||
// Update tracking - don't advance offset for packed bitfields
|
||||
if field_advances_offset {
|
||||
|
||||
@@ -312,7 +312,8 @@ impl PyCUnionType {
|
||||
PyCField::new(name.clone(), field_type_ref, 0, size as isize, index)
|
||||
};
|
||||
|
||||
cls.set_attr(vm.ctx.intern_str(name), c_field.to_pyobject(vm));
|
||||
cls.as_object()
|
||||
.set_attr(vm.ctx.intern_str(name), c_field.to_pyobject(vm), vm)?;
|
||||
}
|
||||
|
||||
// Calculate total_align and aligned_size
|
||||
|
||||
@@ -840,6 +840,7 @@ pub mod module {
|
||||
reinit_mutex_after_fork(&vm.state.atexit_funcs);
|
||||
reinit_mutex_after_fork(&vm.state.global_trace_func);
|
||||
reinit_mutex_after_fork(&vm.state.global_profile_func);
|
||||
reinit_mutex_after_fork(&vm.state.type_mutex);
|
||||
reinit_mutex_after_fork(&vm.state.monitoring);
|
||||
|
||||
// PyGlobalState parking_lot::Mutex locks
|
||||
|
||||
@@ -115,6 +115,7 @@ where
|
||||
switch_interval: AtomicCell::new(0.005),
|
||||
global_trace_func: PyMutex::default(),
|
||||
global_profile_func: PyMutex::default(),
|
||||
type_mutex: PyMutex::default(),
|
||||
#[cfg(feature = "threading")]
|
||||
main_thread_ident: AtomicCell::new(0),
|
||||
#[cfg(feature = "threading")]
|
||||
|
||||
@@ -599,6 +599,8 @@ pub struct PyGlobalState {
|
||||
pub global_trace_func: PyMutex<Option<PyObjectRef>>,
|
||||
/// Global profile function for all threads (set by sys._setprofileallthreads)
|
||||
pub global_profile_func: PyMutex<Option<PyObjectRef>>,
|
||||
/// Global type mutation/versioning mutex for CPython-style FT type operations.
|
||||
pub type_mutex: PyMutex<()>,
|
||||
/// Main thread identifier (pthread_self on Unix)
|
||||
#[cfg(feature = "threading")]
|
||||
pub main_thread_ident: AtomicCell<u64>,
|
||||
|
||||
Reference in new issue
Block a user