From 5648815fffea5ef24079df10dda9c4462e7fb92d Mon Sep 17 00:00:00 2001 From: mejrs Date: Fri, 26 Nov 2021 17:16:05 +0100 Subject: [PATCH] part1 --- guide/src/class.md | 108 ----------------- pyo3-macros-backend/src/pyclass.rs | 34 ++---- src/class/impl_.rs | 25 +--- src/pycell.rs | 185 +++++++++++++++++------------ src/types/mod.rs | 2 +- 5 files changed, 126 insertions(+), 228 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 3b31e248..a1b8b872 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -814,115 +814,7 @@ The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplC ```rust # #[cfg(not(feature = "multiple-pymethods"))] { -# use pyo3::prelude::*; -// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled. -struct MyClass { - # #[allow(dead_code)] - num: i32, -} -unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass { - type AsRefTarget = ::pyo3::PyCell; - const NAME: &'static str = "MyClass"; - const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None; - #[inline] - fn type_object_raw(py: ::pyo3::Python<'_>) -> *mut ::pyo3::ffi::PyTypeObject { - use ::pyo3::type_object::LazyStaticType; - static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); - TYPE_OBJECT.get_or_init::(py) - } -} -impl ::pyo3::PyClass for MyClass { - type Dict = ::pyo3::pyclass_slots::PyClassDummySlot; - type WeakRef = ::pyo3::pyclass_slots::PyClassDummySlot; - type BaseNativeType = ::pyo3::PyAny; -} - -unsafe impl ::pyo3::pyclass::MutablePyClass for MyClass {} - -unsafe impl ::pyo3::class::impl_::BorrowImpl for MyClass {} - -impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut MyClass { - type Target = ::pyo3::PyRefMut<'a, MyClass>; -} - -impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a MyClass { - type Target = ::pyo3::PyRef<'a, MyClass>; -} - -impl ::pyo3::IntoPy<::pyo3::PyObject> for MyClass { - fn into_py(self, py: ::pyo3::Python) -> ::pyo3::PyObject { - ::pyo3::IntoPy::into_py(::pyo3::Py::new(py, self).unwrap(), py) - } -} - -impl ::pyo3::class::impl_::PyClassImpl for MyClass { - const DOC: &'static str = "\u{0}"; - const IS_GC: bool = false; - const IS_BASETYPE: bool = false; - const IS_SUBCLASS: bool = false; - type Layout = ::pyo3::PyCell; - type BaseType = ::pyo3::PyAny; - type ThreadChecker = ::pyo3::class::impl_::ThreadCheckerStub; - fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - visitor(collector.py_methods()); - visitor(collector.py_class_descriptors()); - visitor(collector.object_protocol_methods()); - visitor(collector.async_protocol_methods()); - visitor(collector.descr_protocol_methods()); - visitor(collector.mapping_protocol_methods()); - visitor(collector.number_protocol_methods()); - } - fn get_new() -> ::std::option::Option<::pyo3::ffi::newfunc> { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - collector.new_impl() - } - fn get_alloc() -> ::std::option::Option<::pyo3::ffi::allocfunc> { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - collector.alloc_impl() - } - fn get_free() -> ::std::option::Option<::pyo3::ffi::freefunc> { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - collector.free_impl() - } - fn for_each_proto_slot(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::ffi::PyType_Slot])) { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - visitor(collector.object_protocol_slots()); - visitor(collector.number_protocol_slots()); - visitor(collector.iter_protocol_slots()); - visitor(collector.gc_protocol_slots()); - visitor(collector.descr_protocol_slots()); - visitor(collector.mapping_protocol_slots()); - visitor(collector.sequence_protocol_slots()); - visitor(collector.async_protocol_slots()); - visitor(collector.buffer_protocol_slots()); - visitor(collector.methods_protocol_slots()); - } - fn get_buffer() -> ::std::option::Option<&'static ::pyo3::class::impl_::PyBufferProcs> { - use ::pyo3::class::impl_::*; - let collector = PyClassImplCollector::::new(); - collector.buffer_procs() - } -} - -impl ::pyo3::class::impl_::PyClassDescriptors - for ::pyo3::class::impl_::PyClassImplCollector -{ - fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] { - static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[]; - METHODS - } -} -# Python::with_gil(|py| { -# let cls = py.get_type::(); -# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'") -# }); # } ``` diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 8b5e8088..21b8c2dd 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -760,6 +760,17 @@ impl<'a> PyClassImplsBuilder<'a> { } }, }; + + let mutability = if self.attr.is_immutable { + quote! { + ::pyo3::pycell::Immutable + } + } else { + quote! { + ::pyo3::pycell::Mutable + } + }; + quote! { impl ::pyo3::class::impl_::PyClassImpl for #cls { const DOC: &'static str = #doc; @@ -770,6 +781,7 @@ impl<'a> PyClassImplsBuilder<'a> { type Layout = ::pyo3::PyCell; type BaseType = #base; type ThreadChecker = #thread_checker; + type Mutability= #mutability; fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) { use ::pyo3::class::impl_::*; @@ -828,30 +840,10 @@ impl<'a> PyClassImplsBuilder<'a> { if self.attr.is_immutable { quote! { unsafe impl ::pyo3::pyclass::ImmutablePyClass for #cls {} - - unsafe impl ::pyo3::class::impl_::BorrowImpl for #cls { - fn get_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell) -> ::pyo3::pycell::BorrowFlag - where Self: ::pyo3::PyClass - { - ::pyo3::pycell::impl_::get_borrow_flag_dummy - } - fn increment_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell, ::pyo3::pycell::BorrowFlag) - where Self: ::pyo3::PyClass - { - ::pyo3::pycell::impl_::increment_borrow_flag_dummy - } - fn decrement_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell, ::pyo3::pycell::BorrowFlag) - where Self: ::pyo3::PyClass - { - ::pyo3::pycell::impl_::decrement_borrow_flag_dummy - } - } - } + } } else { quote! { unsafe impl ::pyo3::pyclass::MutablePyClass for #cls {} - - unsafe impl ::pyo3::class::impl_::BorrowImpl for #cls {} } } } diff --git a/src/class/impl_.rs b/src/class/impl_.rs index 84258801..ddcfeef5 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -39,7 +39,7 @@ impl Copy for PyClassImplCollector {} /// /// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail /// and may be changed at any time. -pub trait PyClassImpl: Sized + BorrowImpl { +pub trait PyClassImpl: Sized { /// Class doc string const DOC: &'static str = "\0"; @@ -67,6 +67,8 @@ pub trait PyClassImpl: Sized + BorrowImpl { /// can be accessed by multiple threads by `threading` module. type ThreadChecker: PyClassThreadChecker; + type Mutability: crate::pycell::Mutability; + fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {} fn get_new() -> Option { None @@ -83,27 +85,6 @@ pub trait PyClassImpl: Sized + BorrowImpl { } } -pub unsafe trait BorrowImpl { - fn get_borrow_flag() -> for<'r> fn(&'r pycell::PyCell) -> pycell::BorrowFlag - where - Self: PyClass, - { - pycell::impl_::get_borrow_flag - } - fn increment_borrow_flag() -> for<'r> fn(&'r pycell::PyCell, pycell::BorrowFlag) - where - Self: PyClass, - { - pycell::impl_::increment_borrow_flag - } - fn decrement_borrow_flag() -> for<'r> fn(&'r pycell::PyCell, pycell::BorrowFlag) - where - Self: PyClass, - { - pycell::impl_::decrement_borrow_flag - } -} - // Traits describing known special methods. pub trait PyClassNewImpl { diff --git a/src/pycell.rs b/src/pycell.rs index caad504f..7159db4f 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -192,17 +192,112 @@ use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; +use std::marker::PhantomData; + +pub trait Mutability { + /// Creates a new borrow checker + fn new() -> Self; + /// Increments immutable borrow count, if possible + fn try_borrow(&self) -> Result<(), PyBorrowError>; + + fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>; + + /// Decrements immutable borrow count + fn release_borrow(&self); + /// Increments mutable borrow count, if possible + fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>; + /// Decremements mutable borrow count + fn release_borrow_mut(&self); +} + +pub struct Mutable { + flag: Cell, +} +impl Mutability for Mutable { + fn new() -> Self{ + Self{flag: Cell::new(BorrowFlag::UNUSED)} + } + + fn try_borrow(&self) -> Result<(), PyBorrowError>{ + let flag = self.flag.get(); + if flag != BorrowFlag::HAS_MUTABLE_BORROW { + self.flag.set(flag.increment()); + Ok(()) + } + else{ + Err(PyBorrowError { _private: () }) + } + } + + fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>{ + let flag = self.flag.get(); + if flag != BorrowFlag::HAS_MUTABLE_BORROW { + Ok(()) + } + else{ + Err(PyBorrowError { _private: () }) + } + } + + fn release_borrow(&self){ + let flag = self.flag.get(); + self.flag.set(flag.decrement()) + } + + fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>{ + let flag = self.flag.get(); + if flag == BorrowFlag::UNUSED { + self.flag.set(BorrowFlag::HAS_MUTABLE_BORROW); + Ok(()) + } + else{ + Err(PyBorrowMutError { _private: () }) + } + } + + fn release_borrow_mut(&self){ + self.flag.set(BorrowFlag::UNUSED) + } +} + +pub struct Immutable{ + flag: PhantomData> +} +impl Mutability for Immutable { + fn new() -> Self{ + Self{flag: PhantomData} + } + + fn try_borrow(&self) -> Result<(), PyBorrowError>{ + Ok(()) + } + + fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>{ + Ok(()) + } + + fn release_borrow(&self){ + } + + fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>{ + unreachable!() + } + + fn release_borrow_mut(&self){ + unreachable!() + } +} /// Base layout of PyCell. /// This is necessary for sharing BorrowFlag between parents and children. #[doc(hidden)] #[repr(C)] -pub struct PyCellBase { +pub struct PyCellBase { ob_base: T, - borrow_flag: Cell, + borrow_impl: M, } -unsafe impl PyLayout for PyCellBase where U: PySizedLayout {} +unsafe impl PyLayout for PyCellBase where U: PySizedLayout, M: Mutability {} /// A container type for (mutably) accessing [`PyClass`] values /// @@ -395,13 +490,7 @@ impl PyCell { /// }); /// ``` pub fn try_borrow(&self) -> Result, PyBorrowError> { - let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self); - if flag == BorrowFlag::HAS_MUTABLE_BORROW { - Err(PyBorrowError { _private: () }) - } else { - crate::class::impl_::BorrowImpl::increment_borrow_flag()(self, flag); - Ok(PyRef { inner: self }) - } + self.borrow_checker().try_borrow().map(|_| PyRef { inner: self }) } /// Mutably borrows the value `T`, returning an error if the value is currently borrowed. @@ -429,12 +518,7 @@ impl PyCell { where T: MutablePyClass, { - if self.get_borrow_flag() != BorrowFlag::UNUSED { - Err(PyBorrowMutError { _private: () }) - } else { - self.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW); - Ok(PyRefMut { inner: self }) - } + self.borrow_checker().try_borrow_mut().map(|_| PyRefMut { inner: self }) } /// Immutably borrows the value `T`, returning an error if the value is @@ -467,13 +551,7 @@ impl PyCell { /// }); /// ``` pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - if crate::class::impl_::BorrowImpl::get_borrow_flag()(self) - == BorrowFlag::HAS_MUTABLE_BORROW - { - Err(PyBorrowError { _private: () }) - } else { - Ok(&*self.contents.value.get()) - } + self.borrow_checker().try_borrow_unguarded().map(|_:()| &*self.contents.value.get()) } /// Replaces the wrapped value with a new one, returning the old value. @@ -521,42 +599,6 @@ impl PyCell { } } -#[doc(hidden)] -pub mod impl_ { - use super::*; - - #[inline] - pub fn get_borrow_flag(slf: &PyCell) -> BorrowFlag { - PyCellLayout::get_borrow_flag(slf) - } - - #[inline] - pub fn get_borrow_flag_dummy(_slf: &PyCell) -> BorrowFlag { - debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED); - BorrowFlag::UNUSED - } - - #[inline] - pub fn increment_borrow_flag(slf: &PyCell, flag: BorrowFlag) { - PyCellLayout::set_borrow_flag(slf, flag.increment()); - } - - #[inline] - pub fn increment_borrow_flag_dummy(_slf: &PyCell, _flag: BorrowFlag) { - debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED); - } - - #[inline] - pub fn decrement_borrow_flag(slf: &PyCell, flag: BorrowFlag) { - PyCellLayout::set_borrow_flag(slf, flag.decrement()); - } - - #[inline] - pub fn decrement_borrow_flag_dummy(_slf: &PyCell, _flag: BorrowFlag) { - debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED); - } -} - unsafe impl PyLayout for PyCell {} impl PySizedLayout for PyCell {} @@ -740,8 +782,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { impl<'p, T: PyClass> Drop for PyRef<'p, T> { fn drop(&mut self) { - let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self.inner); - crate::class::impl_::BorrowImpl::decrement_borrow_flag()(self.inner, flag); + self.inner.borrow_checker().release_borrow() } } @@ -839,7 +880,7 @@ impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> { impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> { fn drop(&mut self) { - self.inner.set_borrow_flag(BorrowFlag::UNUSED) + self.inner.borrow_checker().release_borrow_mut() } } @@ -935,9 +976,7 @@ impl From for PyErr { #[doc(hidden)] pub trait PyCellLayout: PyLayout { - fn get_borrow_flag(&self) -> BorrowFlag; - fn set_borrow_flag(&self, flag: BorrowFlag); - + fn borrow_checker(&self) -> &Mutable; /// Implementation of tp_dealloc. /// # Safety /// - slf must be a valid pointer to an instance of a T or a subclass. @@ -945,16 +984,13 @@ pub trait PyCellLayout: PyLayout { unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python); } -impl PyCellLayout for PyCellBase +impl PyCellLayout for PyCellBase where U: PySizedLayout, T: PyTypeInfo, { - fn get_borrow_flag(&self) -> BorrowFlag { - self.borrow_flag.get() - } - fn set_borrow_flag(&self, flag: BorrowFlag) { - self.borrow_flag.set(flag) + fn borrow_checker(&self) -> &Mutable { + &self.borrow_impl } unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) { @@ -982,12 +1018,9 @@ impl PyCellLayout for PyCell where ::LayoutAsBase: PyCellLayout, { - fn get_borrow_flag(&self) -> BorrowFlag { + fn borrow_checker(&self) -> &Mutable { self.contents.thread_checker.ensure(); - self.ob_base.get_borrow_flag() - } - fn set_borrow_flag(&self, flag: BorrowFlag) { - self.ob_base.set_borrow_flag(flag) + self.ob_base.borrow_checker() } unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) { // Safety: Python only calls tp_dealloc when no references to the object remain. diff --git a/src/types/mod.rs b/src/types/mod.rs index f670a32d..d8285412 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -198,7 +198,7 @@ macro_rules! pyobject_native_type_sized { impl<'a, $($generics,)*> $crate::class::impl_::PyClassBaseType for $name { type Dict = $crate::pyclass_slots::PyClassDummySlot; type WeakRef = $crate::pyclass_slots::PyClassDummySlot; - type LayoutAsBase = $crate::pycell::PyCellBase<$layout>; + type LayoutAsBase = $crate::pycell::PyCellBase<$layout, $crate::pycell::Mutable>; type BaseNativeType = $name; type ThreadChecker = $crate::class::impl_::ThreadCheckerStub<$crate::PyObject>; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer;