Merge pull request #758 from kngwyu/remove-oncecell

Lazy type objects without once_cell
This commit is contained in:
Yuji Kanagawa 2020-02-08 15:01:23 +09:00 committed by GitHub
commit 6069ee1239
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 148 additions and 133 deletions

View file

@ -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"

View file

@ -43,10 +43,10 @@ unsafe impl pyo3::PyTypeInfo for MyClass {
const FLAGS: usize = 0;
#[inline]
fn type_object() -> std::ptr::NonNull<pyo3::ffi::PyTypeObject> {
use pyo3::type_object::LazyTypeObject;
static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new();
TYPE_OBJECT.get_pyclass_type::<Self>()
fn type_object() -> &'static pyo3::ffi::PyTypeObject {
use pyo3::type_object::LazyStaticType;
static TYPE_OBJECT: LazyStaticType = LazyStaticType::new();
TYPE_OBJECT.get_or_init::<Self>()
}
}

View file

@ -382,10 +382,10 @@ fn impl_class(
const FLAGS: usize = #(#flags)|* | #extended;
#[inline]
fn type_object() -> std::ptr::NonNull<pyo3::ffi::PyTypeObject> {
use pyo3::type_object::LazyTypeObject;
static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new();
TYPE_OBJECT.get_pyclass_type::<Self>()
fn type_object() -> &'static pyo3::ffi::PyTypeObject {
use pyo3::type_object::LazyStaticType;
static TYPE_OBJECT: LazyStaticType = LazyStaticType::new();
TYPE_OBJECT.get_or_init::<Self>()
}
}

View file

@ -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,31 +90,26 @@ 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();
let ptr = TYPE_OBJECT
.get_or_init(|| {
let gil = $crate::Python::acquire_gil();
let py = gil.python();
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)
));
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();
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) }
}
@ -178,22 +173,17 @@ 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();
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();
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) }
}

View file

@ -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;

View file

@ -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" {

View file

@ -74,7 +74,7 @@ where
{
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().pop() {
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object().as_ptr() as *mut _);
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object() as *const _ as _);
obj as _
} else {
crate::pyclass::default_alloc::<Self>() as _
@ -90,7 +90,7 @@ where
}
if let Some(obj) = <Self as PyClassWithFreeList>::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),
}

View file

@ -13,17 +13,17 @@ use std::ptr::{self, NonNull};
#[inline]
pub(crate) unsafe fn default_alloc<T: PyTypeInfo>() -> *mut ffi::PyObject {
let tp_ptr = T::type_object().as_ptr();
let type_obj = T::type_object();
if T::FLAGS & type_flags::EXTENDED != 0
&& <T::BaseType as PyTypeInfo>::ConcreteLayout::IS_NATIVE_TYPE
{
let base_tp = <T::BaseType as PyTypeInfo>::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),
}
@ -253,19 +253,14 @@ where
}
#[cfg(not(Py_LIMITED_API))]
pub(crate) fn create_type_object<T>(
pub(crate) fn initialize_type_object<T>(
py: Python,
module_name: Option<&str>,
) -> PyResult<Box<ffi::PyTypeObject>>
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 boxed = Box::new(ffi::PyTypeObject_INIT);
let mut type_object = boxed.as_mut();
let base_type_object = <T::BaseType as PyTypeInfo>::type_object().as_ptr();
// PyPy will segfault if passed only a nul terminator as `tp_doc`.
// ptr::null() is OK though.
if T::DESCRIPTION == "\0" {
@ -274,7 +269,7 @@ where
type_object.tp_doc = T::DESCRIPTION.as_ptr() as *const _;
};
type_object.tp_base = base_type_object;
type_object.tp_base = <T::BaseType as PyTypeInfo>::type_object() as *const _ as _;
let name = match module_name {
Some(module_name) => format!("{}.{}", module_name, T::NAME),
@ -371,7 +366,7 @@ where
// register type object
unsafe {
if ffi::PyType_Ready(type_object) == 0 {
Ok(boxed)
Ok(())
} else {
PyErr::fetch(py).into()
}

View file

@ -1,14 +1,14 @@
// 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::UnsafeCell;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
/// `T: PyObjectLayout<U>` 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 +92,19 @@ pub unsafe trait PyTypeInfo: Sized {
/// Initializer for layout
type Initializer: PyObjectInit<Self>;
/// PyTypeObject instance for this type, guaranteed to be global and initialized.
fn type_object() -> NonNull<ffi::PyTypeObject>;
/// 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,49 +124,80 @@ where
T: PyTypeInfo,
{
fn type_object() -> Py<PyType> {
unsafe { Py::from_borrowed_ptr(<Self as PyTypeInfo>::type_object().as_ptr() as *mut _) }
unsafe { Py::from_borrowed_ptr(<Self as PyTypeInfo>::type_object() as *const _ as _) }
}
}
/// Type used to store static type objects
/// Lazy type object for Exceptions
#[doc(hidden)]
pub struct LazyTypeObject {
cell: OnceCell<NonNull<ffi::PyTypeObject>>,
pub struct LazyHeapType {
value: UnsafeCell<Option<NonNull<ffi::PyTypeObject>>>,
initialized: AtomicBool,
}
impl LazyTypeObject {
impl LazyHeapType {
pub const fn new() -> Self {
Self {
cell: OnceCell::new(),
LazyHeapType {
value: UnsafeCell::new(None),
initialized: AtomicBool::new(false),
}
}
pub fn get_or_init<F>(&self, constructor: F) -> PyResult<NonNull<ffi::PyTypeObject>>
pub fn get_or_init<F>(&self, constructor: F) -> NonNull<ffi::PyTypeObject>
where
F: Fn() -> PyResult<NonNull<ffi::PyTypeObject>>,
F: Fn(Python) -> NonNull<ffi::PyTypeObject>,
{
Ok(*self.cell.get_or_try_init(constructor)?)
}
pub fn get_pyclass_type<T: PyClass>(&self) -> NonNull<ffi::PyTypeObject> {
self.get_or_init(|| {
// automatically initialize the class on-demand
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();
let py = gil.python();
let boxed = create_type_object::<T>(py, T::MODULE)?;
Ok(unsafe { NonNull::new_unchecked(Box::into_raw(boxed)) })
})
.unwrap_or_else(|e| {
let gil = Python::acquire_gil();
let py = gil.python();
e.print(py);
panic!("An error occurred while initializing class {}", T::NAME)
})
unsafe {
*self.value.get() = Some(constructor(gil.python()));
}
}
unsafe { (*self.value.get()).unwrap() }
}
}
// This is necessary for making static `LazyTypeObject`s
// 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 LazyTypeObject {}
unsafe impl Sync for LazyHeapType {}
/// Lazy type object for PyClass
#[doc(hidden)]
pub struct LazyStaticType {
value: UnsafeCell<ffi::PyTypeObject>,
initialized: AtomicBool,
}
impl LazyStaticType {
pub const fn new() -> Self {
LazyStaticType {
value: UnsafeCell::new(ffi::PyTypeObject_INIT),
initialized: AtomicBool::new(false),
}
}
pub fn get_or_init<T: PyClass>(&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::<T>(py, T::MODULE, unsafe { &mut *self.value.get() })
.unwrap_or_else(|e| {
e.print(py);
panic!("An error occurred while initializing class {}", T::NAME)
});
}
unsafe { &*self.value.get() }
}
}
// This is necessary for making static `LazyStaticType`s
unsafe impl Sync for LazyStaticType {}

View file

@ -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)]