From f8de335b0041ed270be6597def617c88a6b4dfdd Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 6 Feb 2020 19:10:02 +0900 Subject: [PATCH 1/4] Make PyTypeInfo::type_object return &'static instead of NonNull --- guide/src/class.md | 4 +-- pyo3-derive-backend/src/pyclass.rs | 6 ++-- src/exceptions.rs | 10 +++--- src/freelist.rs | 4 +-- src/pyclass.rs | 32 ++++++++--------- src/type_object.rs | 57 +++++++++++++++++++----------- src/types/mod.rs | 4 +-- 7 files changed, 65 insertions(+), 52 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 47e787774bb..d51a547edcb 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -44,8 +44,8 @@ unsafe impl pyo3::PyTypeInfo for MyClass { #[inline] fn type_object() -> std::ptr::NonNull { - use pyo3::type_object::LazyTypeObject; - static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new(); + use pyo3::type_object::LazyStaticType; + static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); TYPE_OBJECT.get_pyclass_type::() } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index e2f94340d78..cb92c78215e 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -382,9 +382,9 @@ fn impl_class( const FLAGS: usize = #(#flags)|* | #extended; #[inline] - fn type_object() -> std::ptr::NonNull { - use pyo3::type_object::LazyTypeObject; - static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new(); + fn type_object() -> &'static pyo3::ffi::PyTypeObject { + use pyo3::type_object::LazyStaticType; + static TYPE_OBJECT: LazyStaticType = LazyStaticType::new_static(); TYPE_OBJECT.get_pyclass_type::() } } diff --git a/src/exceptions.rs b/src/exceptions.rs index e42d265bfaf..ebcad04d70c 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -9,8 +9,8 @@ use crate::types::{PyAny, PyTuple}; use crate::Python; use crate::{AsPyPointer, ToPyObject}; use std::ffi::CStr; +use std::ops; use std::os::raw::c_char; -use std::{self, ops}; /// The boilerplate to convert between a rust type and a python exception #[macro_export] @@ -90,8 +90,8 @@ macro_rules! import_exception_type_object { ($module: expr, $name: ident) => { unsafe impl $crate::type_object::PyTypeObject for $name { fn type_object() -> $crate::Py<$crate::types::PyType> { - use $crate::type_object::LazyTypeObject; - static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new(); + use $crate::type_object::LazyHeapType; + static TYPE_OBJECT: LazyHeapType = LazyHeapType::new_heap(); let ptr = TYPE_OBJECT .get_or_init(|| { @@ -178,8 +178,8 @@ macro_rules! create_exception_type_object { ($module: ident, $name: ident, $base: ty) => { unsafe impl $crate::type_object::PyTypeObject for $name { fn type_object() -> $crate::Py<$crate::types::PyType> { - use $crate::type_object::LazyTypeObject; - static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new(); + use $crate::type_object::LazyHeapType; + static TYPE_OBJECT: LazyHeapType = LazyHeapType::new_heap(); let ptr = TYPE_OBJECT .get_or_init(|| { diff --git a/src/freelist.rs b/src/freelist.rs index 4217839d4c3..9dd44edf7a0 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -74,7 +74,7 @@ where { unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout { if let Some(obj) = ::get_free_list().pop() { - ffi::PyObject_Init(obj, ::type_object().as_ptr() as *mut _); + ffi::PyObject_Init(obj, ::type_object() as *const _ as _); obj as _ } else { crate::pyclass::default_alloc::() as _ @@ -90,7 +90,7 @@ where } if let Some(obj) = ::get_free_list().insert(obj) { - match Self::type_object().as_ref().tp_free { + match Self::type_object().tp_free { Some(free) => free(obj as *mut c_void), None => tp_free_fallback(obj), } diff --git a/src/pyclass.rs b/src/pyclass.rs index 7af34496958..d71375936dc 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -13,17 +13,17 @@ use std::ptr::{self, NonNull}; #[inline] pub(crate) unsafe fn default_alloc() -> *mut ffi::PyObject { - let tp_ptr = T::type_object().as_ptr(); + let type_obj = T::type_object(); if T::FLAGS & type_flags::EXTENDED != 0 && ::ConcreteLayout::IS_NATIVE_TYPE { let base_tp = ::type_object(); - if let Some(base_new) = base_tp.as_ref().tp_new { - return base_new(tp_ptr, ptr::null_mut(), ptr::null_mut()); + if let Some(base_new) = base_tp.tp_new { + return base_new(type_obj as *const _ as _, ptr::null_mut(), ptr::null_mut()); } } - let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc); - alloc(tp_ptr, 0) + let alloc = type_obj.tp_alloc.unwrap_or(ffi::PyType_GenericAlloc); + alloc(type_obj as *const _ as _, 0) } /// This trait enables custom alloc/dealloc implementations for `T: PyClass`. @@ -47,7 +47,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { return; } - match Self::type_object().as_ref().tp_free { + match Self::type_object().tp_free { Some(free) => free(obj as *mut c_void), None => tp_free_fallback(obj), } @@ -262,9 +262,7 @@ where { // Box (or some other heap allocation) is needed because PyType_Ready expects the type object // to have a permanent memory address. - let mut boxed = Box::new(ffi::PyTypeObject_INIT); - let mut type_object = boxed.as_mut(); - let base_type_object = ::type_object().as_ptr(); + let mut type_object = Box::new(ffi::PyTypeObject_INIT); // PyPy will segfault if passed only a nul terminator as `tp_doc`. // ptr::null() is OK though. @@ -274,7 +272,7 @@ where type_object.tp_doc = T::DESCRIPTION.as_ptr() as *const _; }; - type_object.tp_base = base_type_object; + type_object.tp_base = ::type_object() as *const _ as _; let name = match module_name { Some(module_name) => format!("{}.{}", module_name, T::NAME), @@ -312,16 +310,16 @@ where } // GC support - ::update_type_object(type_object); + ::update_type_object(&mut type_object); // descriptor protocol - ::tp_as_descr(type_object); + ::tp_as_descr(&mut type_object); // iterator methods - ::tp_as_iter(type_object); + ::tp_as_iter(&mut type_object); // basic methods - ::tp_as_object(type_object); + ::tp_as_object(&mut type_object); fn to_ptr(value: Option) -> *mut T { value @@ -366,12 +364,12 @@ where } // set type flags - py_class_flags::(type_object); + py_class_flags::(&mut type_object); // register type object unsafe { - if ffi::PyType_Ready(type_object) == 0 { - Ok(boxed) + if ffi::PyType_Ready(type_object.as_mut()) == 0 { + Ok(type_object) } else { PyErr::fetch(py).into() } diff --git a/src/type_object.rs b/src/type_object.rs index 9ca41dad111..d314489c736 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -8,7 +8,6 @@ use crate::pyclass_init::PyObjectInit; use crate::types::{PyAny, PyType}; use crate::{ffi, AsPyPointer, Python}; use once_cell::sync::OnceCell; -use std::ptr::NonNull; /// `T: PyObjectLayout` represents that `T` is a concrete representaion of `U` in Python heap. /// E.g., `PyClassShell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` @@ -92,19 +91,19 @@ pub unsafe trait PyTypeInfo: Sized { /// Initializer for layout type Initializer: PyObjectInit; - /// PyTypeObject instance for this type, guaranteed to be global and initialized. - fn type_object() -> NonNull; + /// PyTypeObject instance for this type. + fn type_object() -> &'static ffi::PyTypeObject; /// Check if `*mut ffi::PyObject` is instance of this type fn is_instance(object: &PyAny) -> bool { unsafe { - ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object().as_ptr() as *mut _) != 0 + ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object() as *const _ as _) != 0 } } /// Check if `*mut ffi::PyObject` is exact instance of this type fn is_exact_instance(object: &PyAny) -> bool { - unsafe { (*object.as_ptr()).ob_type == Self::type_object().as_ptr() as *mut _ } + unsafe { (*object.as_ptr()).ob_type == Self::type_object() as *const _ as _ } } } @@ -124,42 +123,58 @@ where T: PyTypeInfo, { fn type_object() -> Py { - unsafe { Py::from_borrowed_ptr(::type_object().as_ptr() as *mut _) } + unsafe { Py::from_borrowed_ptr(::type_object() as *const _ as _) } } } /// Type used to store static type objects #[doc(hidden)] -pub struct LazyTypeObject { - cell: OnceCell>, +pub struct LazyTypeObject { + cell: OnceCell, } -impl LazyTypeObject { - pub const fn new() -> Self { - Self { - cell: OnceCell::new(), - } - } +/// For exceptions. +#[doc(hidden)] +pub type LazyHeapType = LazyTypeObject>; - pub fn get_or_init(&self, constructor: F) -> PyResult> +/// For pyclass. +#[doc(hidden)] +pub type LazyStaticType = LazyTypeObject<&'static ffi::PyTypeObject>; + +impl LazyTypeObject { + pub fn get_or_init(&self, constructor: F) -> PyResult where - F: Fn() -> PyResult>, + F: Fn() -> PyResult, { Ok(*self.cell.get_or_try_init(constructor)?) } +} - pub fn get_pyclass_type(&self) -> NonNull { +impl LazyHeapType { + pub const fn new_heap() -> Self { + Self { + cell: OnceCell::new(), + } + } +} + +impl LazyStaticType { + pub const fn new_static() -> Self { + Self { + cell: OnceCell::new(), + } + } + pub fn get_pyclass_type(&self) -> &'static ffi::PyTypeObject { self.get_or_init(|| { // automatically initialize the class on-demand let gil = Python::acquire_gil(); let py = gil.python(); let boxed = create_type_object::(py, T::MODULE)?; - Ok(unsafe { NonNull::new_unchecked(Box::into_raw(boxed)) }) + Ok(Box::leak(boxed)) }) .unwrap_or_else(|e| { let gil = Python::acquire_gil(); - let py = gil.python(); - e.print(py); + e.print(gil.python()); panic!("An error occurred while initializing class {}", T::NAME) }) } @@ -169,4 +184,4 @@ impl LazyTypeObject { // // Type objects are shared between threads by the Python interpreter anyway, so it is no worse // to allow sharing on the Rust side too. -unsafe impl Sync for LazyTypeObject {} +unsafe impl Sync for LazyTypeObject {} diff --git a/src/types/mod.rs b/src/types/mod.rs index 920049874df..cde4a8601cc 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -124,8 +124,8 @@ macro_rules! pyobject_native_type_convert( const MODULE: Option<&'static str> = $module; #[inline] - fn type_object() -> std::ptr::NonNull<$crate::ffi::PyTypeObject> { - unsafe { std::ptr::NonNull::new_unchecked(&mut $typeobject as *mut _) } + fn type_object() -> &'static $crate::ffi::PyTypeObject { + unsafe{ &$typeobject } } #[allow(unused_unsafe)] From 58a0841ff788c5255764bb4ecca21c97139365f7 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 6 Feb 2020 23:27:59 +0900 Subject: [PATCH 2/4] Rewrite LazyTypeObjects without once_cell --- Cargo.toml | 1 - guide/src/class.md | 4 +- pyo3-derive-backend/src/pyclass.rs | 4 +- src/exceptions.rs | 68 ++++++++++------------ src/pyclass.rs | 23 ++++---- src/type_object.rs | 91 +++++++++++++++--------------- 6 files changed, 90 insertions(+), 101 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89bd6e39bc2..386864e4cac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ parking_lot = { version = "0.10", features = ["nightly"] } paste = "0.1.6" pyo3cls = { path = "pyo3cls", version = "=0.9.0-alpha.1" } unindent = "0.1.4" -once_cell = "1.3.1" [dev-dependencies] assert_approx_eq = "1.1.0" diff --git a/guide/src/class.md b/guide/src/class.md index d51a547edcb..a202ada55ce 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -43,10 +43,10 @@ unsafe impl pyo3::PyTypeInfo for MyClass { const FLAGS: usize = 0; #[inline] - fn type_object() -> std::ptr::NonNull { + fn type_object() -> &'static pyo3::ffi::PyTypeObject { use pyo3::type_object::LazyStaticType; static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); - TYPE_OBJECT.get_pyclass_type::() + TYPE_OBJECT.get::() } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index cb92c78215e..070339a038d 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -384,8 +384,8 @@ fn impl_class( #[inline] fn type_object() -> &'static pyo3::ffi::PyTypeObject { use pyo3::type_object::LazyStaticType; - static TYPE_OBJECT: LazyStaticType = LazyStaticType::new_static(); - TYPE_OBJECT.get_pyclass_type::() + static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); + TYPE_OBJECT.get::() } } diff --git a/src/exceptions.rs b/src/exceptions.rs index ebcad04d70c..5261477016b 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -91,30 +91,25 @@ macro_rules! import_exception_type_object { unsafe impl $crate::type_object::PyTypeObject for $name { fn type_object() -> $crate::Py<$crate::types::PyType> { use $crate::type_object::LazyHeapType; - static TYPE_OBJECT: LazyHeapType = LazyHeapType::new_heap(); - - let ptr = TYPE_OBJECT - .get_or_init(|| { - let gil = $crate::Python::acquire_gil(); - let py = gil.python(); - - let imp = py - .import(stringify!($module)) - .expect(concat!("Can not import module: ", stringify!($module))); - let cls = imp.get(stringify!($name)).expect(concat!( - "Can not load exception class: {}.{}", - stringify!($module), - ".", - stringify!($name) - )); - - unsafe { - Ok(std::ptr::NonNull::new_unchecked( - $crate::IntoPyPointer::into_ptr(cls) as *mut _, - )) - } - }) - .unwrap(); + static TYPE_OBJECT: LazyHeapType = LazyHeapType::new(); + + let ptr = TYPE_OBJECT.get_or_init(|py| { + let imp = py + .import(stringify!($module)) + .expect(concat!("Can not import module: ", stringify!($module))); + let cls = imp.get(stringify!($name)).expect(concat!( + "Can not load exception class: {}.{}", + stringify!($module), + ".", + stringify!($name) + )); + + unsafe { + std::ptr::NonNull::new_unchecked( + $crate::IntoPyPointer::into_ptr(cls) as *mut _ + ) + } + }); unsafe { $crate::Py::from_borrowed_ptr(ptr.as_ptr() as *mut $crate::ffi::PyObject) } } @@ -179,21 +174,16 @@ macro_rules! create_exception_type_object { unsafe impl $crate::type_object::PyTypeObject for $name { fn type_object() -> $crate::Py<$crate::types::PyType> { use $crate::type_object::LazyHeapType; - static TYPE_OBJECT: LazyHeapType = LazyHeapType::new_heap(); - - let ptr = TYPE_OBJECT - .get_or_init(|| { - let gil = $crate::Python::acquire_gil(); - let py = gil.python(); - - Ok($crate::PyErr::new_type( - py, - concat!(stringify!($module), ".", stringify!($name)), - Some(py.get_type::<$base>()), - None, - )) - }) - .unwrap(); + static TYPE_OBJECT: LazyHeapType = LazyHeapType::new(); + + let ptr = TYPE_OBJECT.get_or_init(|py| { + $crate::PyErr::new_type( + py, + concat!(stringify!($module), ".", stringify!($name)), + Some(py.get_type::<$base>()), + None, + ) + }); unsafe { $crate::Py::from_borrowed_ptr(ptr.as_ptr() as *mut $crate::ffi::PyObject) } } diff --git a/src/pyclass.rs b/src/pyclass.rs index d71375936dc..12fb801bd36 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -253,17 +253,14 @@ where } #[cfg(not(Py_LIMITED_API))] -pub(crate) fn create_type_object( +pub(crate) fn initialize_type_object( py: Python, module_name: Option<&str>, -) -> PyResult> + type_object: &mut ffi::PyTypeObject, +) -> PyResult<()> where T: PyClass, { - // Box (or some other heap allocation) is needed because PyType_Ready expects the type object - // to have a permanent memory address. - let mut type_object = Box::new(ffi::PyTypeObject_INIT); - // PyPy will segfault if passed only a nul terminator as `tp_doc`. // ptr::null() is OK though. if T::DESCRIPTION == "\0" { @@ -310,16 +307,16 @@ where } // GC support - ::update_type_object(&mut type_object); + ::update_type_object(type_object); // descriptor protocol - ::tp_as_descr(&mut type_object); + ::tp_as_descr(type_object); // iterator methods - ::tp_as_iter(&mut type_object); + ::tp_as_iter(type_object); // basic methods - ::tp_as_object(&mut type_object); + ::tp_as_object(type_object); fn to_ptr(value: Option) -> *mut T { value @@ -364,12 +361,12 @@ where } // set type flags - py_class_flags::(&mut type_object); + py_class_flags::(type_object); // register type object unsafe { - if ffi::PyType_Ready(type_object.as_mut()) == 0 { - Ok(type_object) + if ffi::PyType_Ready(type_object) == 0 { + Ok(()) } else { PyErr::fetch(py).into() } diff --git a/src/type_object.rs b/src/type_object.rs index d314489c736..9b8adf8085c 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -1,13 +1,13 @@ // Copyright (c) 2017-present PyO3 Project and Contributors //! Python type object information -use crate::err::PyResult; use crate::instance::Py; -use crate::pyclass::{create_type_object, PyClass}; +use crate::pyclass::{initialize_type_object, PyClass}; use crate::pyclass_init::PyObjectInit; use crate::types::{PyAny, PyType}; use crate::{ffi, AsPyPointer, Python}; -use once_cell::sync::OnceCell; +use std::cell::{Cell, UnsafeCell}; +use std::ptr::NonNull; /// `T: PyObjectLayout` represents that `T` is a concrete representaion of `U` in Python heap. /// E.g., `PyClassShell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` @@ -127,61 +127,64 @@ where } } -/// Type used to store static type objects +/// Lazy type object for Exceptions #[doc(hidden)] -pub struct LazyTypeObject { - cell: OnceCell, -} - -/// For exceptions. -#[doc(hidden)] -pub type LazyHeapType = LazyTypeObject>; +pub struct LazyHeapType(UnsafeCell>>); -/// For pyclass. -#[doc(hidden)] -pub type LazyStaticType = LazyTypeObject<&'static ffi::PyTypeObject>; - -impl LazyTypeObject { - pub fn get_or_init(&self, constructor: F) -> PyResult +impl LazyHeapType { + pub const fn new() -> Self { + Self(UnsafeCell::new(None)) + } + pub fn get_or_init(&self, constructor: F) -> NonNull where - F: Fn() -> PyResult, + F: Fn(Python) -> NonNull, { - Ok(*self.cell.get_or_try_init(constructor)?) + if let Some(value) = unsafe { &*self.0.get() } { + return value.clone(); + } + // We have to get the GIL before setting the value to the global!!! + let gil = Python::acquire_gil(); + unsafe { + *self.0.get() = Some(constructor(gil.python())); + (*self.0.get()).unwrap() + } } } -impl LazyHeapType { - pub const fn new_heap() -> Self { - Self { - cell: OnceCell::new(), - } - } +// This is necessary for making static `LazyHeapType`s +// +// Type objects are shared between threads by the Python interpreter anyway, so it is no worse +// to allow sharing on the Rust side too. +unsafe impl Sync for LazyHeapType {} + +/// Lazy type object for PyClass +#[doc(hidden)] +pub struct LazyStaticType { + value: UnsafeCell, + initialized: Cell, } impl LazyStaticType { - pub const fn new_static() -> Self { - Self { - cell: OnceCell::new(), + pub const fn new() -> Self { + LazyStaticType { + value: UnsafeCell::new(ffi::PyTypeObject_INIT), + initialized: Cell::new(false), } } - pub fn get_pyclass_type(&self) -> &'static ffi::PyTypeObject { - self.get_or_init(|| { - // automatically initialize the class on-demand + pub fn get(&self) -> &ffi::PyTypeObject { + if !self.initialized.get() { let gil = Python::acquire_gil(); let py = gil.python(); - let boxed = create_type_object::(py, T::MODULE)?; - Ok(Box::leak(boxed)) - }) - .unwrap_or_else(|e| { - let gil = Python::acquire_gil(); - e.print(gil.python()); - panic!("An error occurred while initializing class {}", T::NAME) - }) + initialize_type_object::(py, T::MODULE, unsafe { &mut *self.value.get() }) + .unwrap_or_else(|e| { + e.print(py); + panic!("An error occurred while initializing class {}", T::NAME) + }); + self.initialized.set(true); + } + unsafe { &*self.value.get() } } } -// This is necessary for making static `LazyTypeObject`s -// -// Type objects are shared between threads by the Python interpreter anyway, so it is no worse -// to allow sharing on the Rust side too. -unsafe impl Sync for LazyTypeObject {} +// This is necessary for making static `LazyStaticType`s +unsafe impl Sync for LazyStaticType {} From 50de21abb720de8a7ae98f847a516ccbe9ac9c57 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 8 Feb 2020 13:43:12 +0900 Subject: [PATCH 3/4] Use AtomicBool for lazy types --- guide/src/class.md | 2 +- pyo3-derive-backend/src/pyclass.rs | 2 +- src/type_object.rs | 45 +++++++++++++++++++----------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index a202ada55ce..9215c6a9eb1 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -46,7 +46,7 @@ unsafe impl pyo3::PyTypeInfo for MyClass { fn type_object() -> &'static pyo3::ffi::PyTypeObject { use pyo3::type_object::LazyStaticType; static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); - TYPE_OBJECT.get::() + TYPE_OBJECT.get_or_init::() } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index 070339a038d..19b01b2502f 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -385,7 +385,7 @@ fn impl_class( fn type_object() -> &'static pyo3::ffi::PyTypeObject { use pyo3::type_object::LazyStaticType; static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); - TYPE_OBJECT.get::() + TYPE_OBJECT.get_or_init::() } } diff --git a/src/type_object.rs b/src/type_object.rs index 9b8adf8085c..6b12542a924 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -6,8 +6,9 @@ use crate::pyclass::{initialize_type_object, PyClass}; use crate::pyclass_init::PyObjectInit; use crate::types::{PyAny, PyType}; use crate::{ffi, AsPyPointer, Python}; -use std::cell::{Cell, UnsafeCell}; +use std::cell::UnsafeCell; use std::ptr::NonNull; +use std::sync::atomic::{AtomicBool, Ordering}; /// `T: PyObjectLayout` represents that `T` is a concrete representaion of `U` in Python heap. /// E.g., `PyClassShell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` @@ -129,25 +130,34 @@ where /// Lazy type object for Exceptions #[doc(hidden)] -pub struct LazyHeapType(UnsafeCell>>); +pub struct LazyHeapType { + value: UnsafeCell>>, + initialized: AtomicBool, +} impl LazyHeapType { pub const fn new() -> Self { - Self(UnsafeCell::new(None)) + LazyHeapType { + value: UnsafeCell::new(None), + initialized: AtomicBool::new(false), + } } + pub fn get_or_init(&self, constructor: F) -> NonNull where F: Fn(Python) -> NonNull, { - if let Some(value) = unsafe { &*self.0.get() } { - return value.clone(); - } - // We have to get the GIL before setting the value to the global!!! - let gil = Python::acquire_gil(); - unsafe { - *self.0.get() = Some(constructor(gil.python())); - (*self.0.get()).unwrap() + if !self + .initialized + .compare_and_swap(false, true, Ordering::Acquire) + { + // We have to get the GIL before setting the value to the global!!! + let gil = Python::acquire_gil(); + unsafe { + *self.value.get() = Some(constructor(gil.python())); + } } + unsafe { (*self.value.get()).unwrap() } } } @@ -161,18 +171,22 @@ unsafe impl Sync for LazyHeapType {} #[doc(hidden)] pub struct LazyStaticType { value: UnsafeCell, - initialized: Cell, + initialized: AtomicBool, } impl LazyStaticType { pub const fn new() -> Self { LazyStaticType { value: UnsafeCell::new(ffi::PyTypeObject_INIT), - initialized: Cell::new(false), + initialized: AtomicBool::new(false), } } - pub fn get(&self) -> &ffi::PyTypeObject { - if !self.initialized.get() { + + pub fn get_or_init(&self) -> &ffi::PyTypeObject { + if !self + .initialized + .compare_and_swap(false, true, Ordering::Acquire) + { let gil = Python::acquire_gil(); let py = gil.python(); initialize_type_object::(py, T::MODULE, unsafe { &mut *self.value.get() }) @@ -180,7 +194,6 @@ impl LazyStaticType { e.print(py); panic!("An error occurred while initializing class {}", T::NAME) }); - self.initialized.set(true); } unsafe { &*self.value.get() } } From ba20b0ef21528866f0a7a9714ac76e3376678088 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 8 Feb 2020 14:27:59 +0900 Subject: [PATCH 4/4] Remove unnecessary parentheses from FFI --- src/ffi/object.rs | 60 ++++++++++++++++++++--------------------- src/ffi/structmember.rs | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/ffi/object.rs b/src/ffi/object.rs index b06c45b4f96..066567a46c3 100644 --- a/src/ffi/object.rs +++ b/src/ffi/object.rs @@ -177,23 +177,23 @@ mod bufferinfo { pub const PyBUF_WRITEABLE: c_int = PyBUF_WRITABLE; pub const PyBUF_FORMAT: c_int = 0x0004; pub const PyBUF_ND: c_int = 0x0008; - pub const PyBUF_STRIDES: c_int = (0x0010 | PyBUF_ND); - pub const PyBUF_C_CONTIGUOUS: c_int = (0x0020 | PyBUF_STRIDES); - pub const PyBUF_F_CONTIGUOUS: c_int = (0x0040 | PyBUF_STRIDES); - pub const PyBUF_ANY_CONTIGUOUS: c_int = (0x0080 | PyBUF_STRIDES); - pub const PyBUF_INDIRECT: c_int = (0x0100 | PyBUF_STRIDES); + pub const PyBUF_STRIDES: c_int = 0x0010 | PyBUF_ND; + pub const PyBUF_C_CONTIGUOUS: c_int = 0x0020 | PyBUF_STRIDES; + pub const PyBUF_F_CONTIGUOUS: c_int = 0x0040 | PyBUF_STRIDES; + pub const PyBUF_ANY_CONTIGUOUS: c_int = 0x0080 | PyBUF_STRIDES; + pub const PyBUF_INDIRECT: c_int = 0x0100 | PyBUF_STRIDES; - pub const PyBUF_CONTIG: c_int = (PyBUF_ND | PyBUF_WRITABLE); - pub const PyBUF_CONTIG_RO: c_int = (PyBUF_ND); + pub const PyBUF_CONTIG: c_int = PyBUF_ND | PyBUF_WRITABLE; + pub const PyBUF_CONTIG_RO: c_int = PyBUF_ND; - pub const PyBUF_STRIDED: c_int = (PyBUF_STRIDES | PyBUF_WRITABLE); - pub const PyBUF_STRIDED_RO: c_int = (PyBUF_STRIDES); + pub const PyBUF_STRIDED: c_int = PyBUF_STRIDES | PyBUF_WRITABLE; + pub const PyBUF_STRIDED_RO: c_int = PyBUF_STRIDES; - pub const PyBUF_RECORDS: c_int = (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT); - pub const PyBUF_RECORDS_RO: c_int = (PyBUF_STRIDES | PyBUF_FORMAT); + pub const PyBUF_RECORDS: c_int = PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT; + pub const PyBUF_RECORDS_RO: c_int = PyBUF_STRIDES | PyBUF_FORMAT; - pub const PyBUF_FULL: c_int = (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT); - pub const PyBUF_FULL_RO: c_int = (PyBUF_INDIRECT | PyBUF_FORMAT); + pub const PyBUF_FULL: c_int = PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT; + pub const PyBUF_FULL_RO: c_int = PyBUF_INDIRECT | PyBUF_FORMAT; pub const PyBUF_READ: c_int = 0x100; pub const PyBUF_WRITE: c_int = 0x200; @@ -848,42 +848,42 @@ extern "C" { pub const Py_PRINT_RAW: c_int = 1; // No string quotes etc. /// Set if the type object is dynamically allocated -pub const Py_TPFLAGS_HEAPTYPE: c_ulong = (1 << 9); +pub const Py_TPFLAGS_HEAPTYPE: c_ulong = 1 << 9; /// Set if the type allows subclassing -pub const Py_TPFLAGS_BASETYPE: c_ulong = (1 << 10); +pub const Py_TPFLAGS_BASETYPE: c_ulong = 1 << 10; /// Set if the type implements the vectorcall protocol (PEP 590) #[cfg(all(Py_3_8, not(Py_LIMITED_API)))] -pub const _Py_TPFLAGS_HAVE_VECTORCALL: c_ulong = (1 << 11); +pub const _Py_TPFLAGS_HAVE_VECTORCALL: c_ulong = 1 << 11; /// Set if the type is 'ready' -- fully initialized -pub const Py_TPFLAGS_READY: c_ulong = (1 << 12); +pub const Py_TPFLAGS_READY: c_ulong = 1 << 12; /// Set while the type is being 'readied', to prevent recursive ready calls -pub const Py_TPFLAGS_READYING: c_ulong = (1 << 13); +pub const Py_TPFLAGS_READYING: c_ulong = 1 << 13; /// Objects support garbage collection (see objimp.h) -pub const Py_TPFLAGS_HAVE_GC: c_ulong = (1 << 14); +pub const Py_TPFLAGS_HAVE_GC: c_ulong = 1 << 14; const Py_TPFLAGS_HAVE_STACKLESS_EXTENSION: c_ulong = 0; /// Objects support type attribute cache -pub const Py_TPFLAGS_HAVE_VERSION_TAG: c_ulong = (1 << 18); -pub const Py_TPFLAGS_VALID_VERSION_TAG: c_ulong = (1 << 19); +pub const Py_TPFLAGS_HAVE_VERSION_TAG: c_ulong = 1 << 18; +pub const Py_TPFLAGS_VALID_VERSION_TAG: c_ulong = 1 << 19; /* Type is abstract and cannot be instantiated */ -pub const Py_TPFLAGS_IS_ABSTRACT: c_ulong = (1 << 20); +pub const Py_TPFLAGS_IS_ABSTRACT: c_ulong = 1 << 20; /* These flags are used to determine if a type is a subclass. */ -pub const Py_TPFLAGS_LONG_SUBCLASS: c_ulong = (1 << 24); -pub const Py_TPFLAGS_LIST_SUBCLASS: c_ulong = (1 << 25); -pub const Py_TPFLAGS_TUPLE_SUBCLASS: c_ulong = (1 << 26); -pub const Py_TPFLAGS_BYTES_SUBCLASS: c_ulong = (1 << 27); -pub const Py_TPFLAGS_UNICODE_SUBCLASS: c_ulong = (1 << 28); -pub const Py_TPFLAGS_DICT_SUBCLASS: c_ulong = (1 << 29); -pub const Py_TPFLAGS_BASE_EXC_SUBCLASS: c_ulong = (1 << 30); -pub const Py_TPFLAGS_TYPE_SUBCLASS: c_ulong = (1 << 31); +pub const Py_TPFLAGS_LONG_SUBCLASS: c_ulong = 1 << 24; +pub const Py_TPFLAGS_LIST_SUBCLASS: c_ulong = 1 << 25; +pub const Py_TPFLAGS_TUPLE_SUBCLASS: c_ulong = 1 << 26; +pub const Py_TPFLAGS_BYTES_SUBCLASS: c_ulong = 1 << 27; +pub const Py_TPFLAGS_UNICODE_SUBCLASS: c_ulong = 1 << 28; +pub const Py_TPFLAGS_DICT_SUBCLASS: c_ulong = 1 << 29; +pub const Py_TPFLAGS_BASE_EXC_SUBCLASS: c_ulong = 1 << 30; +pub const Py_TPFLAGS_TYPE_SUBCLASS: c_ulong = 1 << 31; pub const Py_TPFLAGS_DEFAULT: c_ulong = Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG; diff --git a/src/ffi/structmember.rs b/src/ffi/structmember.rs index b5749ae64a1..de2b86a65b4 100644 --- a/src/ffi/structmember.rs +++ b/src/ffi/structmember.rs @@ -49,7 +49,7 @@ pub const T_NONE: c_int = 20; /* Value is always None */ pub const READONLY: c_int = 1; pub const READ_RESTRICTED: c_int = 2; pub const PY_WRITE_RESTRICTED: c_int = 4; -pub const RESTRICTED: c_int = (READ_RESTRICTED | PY_WRITE_RESTRICTED); +pub const RESTRICTED: c_int = READ_RESTRICTED | PY_WRITE_RESTRICTED; #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" {