Skip to content

Commit

Permalink
Remove static mut from PyTypeInfo implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jan 30, 2020
1 parent 99a7856 commit 5cbdef6
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 4 additions & 3 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
7 changes: 4 additions & 3 deletions pyo3-derive-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
27 changes: 27 additions & 0 deletions src/derive_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
Expand Down Expand Up @@ -199,3 +201,28 @@ impl<T: PyClass, I: Into<PyClassInitializer<T>>> IntoPyNewResult<T, I> for PyRes
self
}
}

/// Type used to store type objects
pub struct LazyTypeObject {
cell: OnceCell<UnsafeCell<ffi::PyTypeObject>>,
}

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 {}
2 changes: 1 addition & 1 deletion src/freelist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ where
}

if let Some(obj) = <Self as PyClassWithFreeList>::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),
}
Expand Down
12 changes: 6 additions & 6 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down Expand Up @@ -88,9 +88,10 @@ where
{
fn init_type() -> NonNull<ffi::PyTypeObject> {
<T::BaseType as PyTypeObject>::init_type();
let type_object = unsafe { <Self as PyTypeInfo>::type_object() };
let type_object = <Self as PyTypeInfo>::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();
Expand Down Expand Up @@ -282,9 +283,8 @@ pub fn initialize_type<T>(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 { <T::BaseType as PyTypeInfo>::type_object() };
let type_object: &mut ffi::PyTypeObject = unsafe { &mut *T::type_object() };
let base_type_object = <T::BaseType as PyTypeInfo>::type_object();

// PyPy will segfault if passed only a nul terminator as `tp_doc`.
// ptr::null() is OK though.
Expand Down
2 changes: 1 addition & 1 deletion src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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(
<Self as $crate::type_object::PyTypeInfo>::type_object() as *mut _
<Self as $crate::type_object::PyTypeInfo>::type_object()
)
}
}
Expand Down

0 comments on commit 5cbdef6

Please sign in to comment.