From 5cbdef6471204aa5de5ded5c3471b08a23941168 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Thu, 30 Jan 2020 00:11:54 +0000 Subject: [PATCH] Remove static mut from PyTypeInfo implementation --- Cargo.toml | 1 + guide/src/class.md | 7 ++++--- pyo3-derive-backend/src/pyclass.rs | 7 ++++--- src/derive_utils.rs | 27 +++++++++++++++++++++++++++ src/freelist.rs | 2 +- src/pyclass.rs | 12 ++++++------ src/type_object.rs | 2 +- src/types/mod.rs | 6 +++--- 8 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 386864e4..89bd6e39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ 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 cbbde8c4..eb00b2e5 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -43,9 +43,10 @@ impl pyo3::PyTypeInfo for MyClass { const FLAGS: usize = 0; #[inline] - unsafe fn type_object() -> &'static mut pyo3::ffi::PyTypeObject { - static mut TYPE_OBJECT: pyo3::ffi::PyTypeObject = pyo3::ffi::PyTypeObject_INIT; - &mut TYPE_OBJECT + fn type_object() -> *mut pyo3::ffi::PyTypeObject { + static TYPE_OBJECT: pyo3::derive_utils::LazyTypeObject = + pyo3::derive_utils::LazyTypeObject::new(); + TYPE_OBJECT.get() } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index d4d9e564..15addd6f 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -384,9 +384,10 @@ fn impl_class( const FLAGS: usize = #(#flags)|* | #extended; #[inline] - unsafe fn type_object() -> &'static mut pyo3::ffi::PyTypeObject { - static mut TYPE_OBJECT: pyo3::ffi::PyTypeObject = pyo3::ffi::PyTypeObject_INIT; - &mut TYPE_OBJECT + fn type_object() -> *mut pyo3::ffi::PyTypeObject { + static TYPE_OBJECT: pyo3::derive_utils::LazyTypeObject = + pyo3::derive_utils::LazyTypeObject::new(); + TYPE_OBJECT.get() } } diff --git a/src/derive_utils.rs b/src/derive_utils.rs index a5e284b5..d136db4c 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -12,6 +12,8 @@ use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::types::{PyAny, PyDict, PyModule, PyTuple}; use crate::{ffi, GILPool, IntoPy, PyObject, Python}; +use once_cell::sync::OnceCell; +use std::cell::UnsafeCell; use std::ptr; /// Description of a python parameter; used for `parse_args()`. @@ -199,3 +201,28 @@ impl>> IntoPyNewResult for PyRes self } } + +/// Type used to store type objects +pub struct LazyTypeObject { + cell: OnceCell>, +} + +impl LazyTypeObject { + pub const fn new() -> Self { + Self { + cell: OnceCell::new(), + } + } + + pub fn get(&self) -> *mut ffi::PyTypeObject { + self.cell + .get_or_init(|| UnsafeCell::new(ffi::PyTypeObject_INIT)) + .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 {} diff --git a/src/freelist.rs b/src/freelist.rs index cb7113eb..939c3d16 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -90,7 +90,7 @@ where } if let Some(obj) = ::get_free_list().insert(obj) { - match Self::type_object().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 715cf0dd..a01585a4 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -47,7 +47,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { return; } - match Self::type_object().tp_free { + match (*Self::type_object()).tp_free { Some(free) => free(obj as *mut c_void), None => tp_free_fallback(obj), } @@ -88,9 +88,10 @@ where { fn init_type() -> NonNull { ::init_type(); - let type_object = unsafe { ::type_object() }; + let type_object = ::type_object(); + let type_flags = unsafe { (*type_object).tp_flags }; - if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 { + if (type_flags & ffi::Py_TPFLAGS_READY) == 0 { // automatically initialize the class on-demand let gil = Python::acquire_gil(); let py = gil.python(); @@ -282,9 +283,8 @@ pub fn initialize_type(py: Python, module_name: Option<&str>) -> PyResult<*mu where T: PyClass, { - let type_object: &mut ffi::PyTypeObject = unsafe { T::type_object() }; - let base_type_object: &mut ffi::PyTypeObject = - unsafe { ::type_object() }; + let type_object: &mut ffi::PyTypeObject = unsafe { &mut *T::type_object() }; + let base_type_object = ::type_object(); // PyPy will segfault if passed only a nul terminator as `tp_doc`. // ptr::null() is OK though. diff --git a/src/type_object.rs b/src/type_object.rs index 1aedc12d..d61dcb5b 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -87,7 +87,7 @@ pub trait PyTypeInfo: Sized { /// PyTypeObject instance for this type, which might still need to /// be initialized - unsafe fn type_object() -> &'static mut ffi::PyTypeObject; + fn type_object() -> *mut ffi::PyTypeObject; /// Check if `*mut ffi::PyObject` is instance of this type fn is_instance(object: &PyAny) -> bool { diff --git a/src/types/mod.rs b/src/types/mod.rs index 3616b718..fddc470d 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -111,8 +111,8 @@ macro_rules! pyobject_native_type_convert( const MODULE: Option<&'static str> = $module; #[inline] - unsafe fn type_object() -> &'static mut $crate::ffi::PyTypeObject { - &mut $typeobject + fn type_object() -> *mut $crate::ffi::PyTypeObject { + unsafe { &mut $typeobject as *mut _ } } #[allow(unused_unsafe)] @@ -127,7 +127,7 @@ macro_rules! pyobject_native_type_convert( fn init_type() -> std::ptr::NonNull<$crate::ffi::PyTypeObject> { unsafe { std::ptr::NonNull::new_unchecked( - ::type_object() as *mut _ + ::type_object() ) } }