Skip to content

Commit

Permalink
New Native Types and lighter GILPool
Browse files Browse the repository at this point in the history
  • Loading branch information
kngwyu committed May 1, 2020
1 parent 746c352 commit eaee671
Show file tree
Hide file tree
Showing 25 changed files with 112 additions and 369 deletions.
28 changes: 7 additions & 21 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Conversions between various states of Rust and Python types and their wrappers.
use crate::err::{self, PyDowncastError, PyResult};
use crate::object::PyObject;
use crate::type_object::{PyDowncastImpl, PyTypeInfo};
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
use crate::{ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyRef, PyRefMut, Python};
use std::ptr::NonNull;
Expand Down Expand Up @@ -311,7 +311,7 @@ where
/// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`.
///
/// This trait is similar to `std::convert::TryFrom`
pub trait PyTryFrom<'v>: Sized + PyDowncastImpl {
pub trait PyTryFrom<'v>: Sized + PyNativeType {
/// Cast from a concrete Python object type to PyObject.
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;

Expand Down Expand Up @@ -348,7 +348,7 @@ where

impl<'v, T> PyTryFrom<'v> for T
where
T: PyDowncastImpl + PyTypeInfo + PyNativeType,
T: PyTypeInfo + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
Expand Down Expand Up @@ -460,28 +460,14 @@ where
T: 'p + crate::PyNativeType,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_owned(py, p)))
gil::register_owned(py, NonNull::new(ptr)?);
Some(&*(ptr as *mut Self))
}
unsafe fn from_borrowed_ptr_or_opt(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_borrowed(py, p)))
}
}

unsafe impl<'p, T> FromPyPointer<'p> for PyCell<T>
where
T: PyClass,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell<T>))
}
unsafe fn from_borrowed_ptr_or_opt(
py: Python<'p>,
_py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell<T>))
NonNull::new(ptr as *mut Self).map(|p| &*p.as_ptr())
}
}

Expand Down
218 changes: 12 additions & 206 deletions src/gil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

//! Interaction with python's global interpreter lock
use crate::{ffi, internal_tricks::Unsendable, PyAny, Python};
use crate::{ffi, internal_tricks::Unsendable, Python};
use std::cell::{Cell, UnsafeCell};
use std::{any, mem::ManuallyDrop, ptr::NonNull, sync};

Expand Down Expand Up @@ -144,8 +144,7 @@ impl Drop for GILGuard {

/// Implementation of release pool
struct ReleasePoolImpl {
owned: ArrayList<NonNull<ffi::PyObject>>,
borrowed: ArrayList<NonNull<ffi::PyObject>>,
owned: Vec<NonNull<ffi::PyObject>>,
pointers: *mut Vec<NonNull<ffi::PyObject>>,
obj: Vec<Box<dyn any::Any>>,
p: parking_lot::Mutex<*mut Vec<NonNull<ffi::PyObject>>>,
Expand All @@ -154,8 +153,7 @@ struct ReleasePoolImpl {
impl ReleasePoolImpl {
fn new() -> Self {
Self {
owned: ArrayList::new(),
borrowed: ArrayList::new(),
owned: Vec::with_capacity(256),
pointers: Box::into_raw(Box::new(Vec::with_capacity(256))),
obj: Vec::with_capacity(8),
p: parking_lot::Mutex::new(Box::into_raw(Box::new(Vec::with_capacity(256)))),
Expand All @@ -180,14 +178,12 @@ impl ReleasePoolImpl {
vec.set_len(0);
}

pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize) {
pub unsafe fn drain(&mut self, _py: Python, owned: usize) {
// Release owned objects(call decref)
while owned < self.owned.len() {
let last = self.owned.pop_back().unwrap();
ffi::Py_DECREF(last.as_ptr());
for i in owned..self.owned.len() {
ffi::Py_DECREF(self.owned[i].as_ptr());
}
// Release borrowed objects(don't call decref)
self.borrowed.truncate(borrowed);
self.owned.truncate(owned);
self.release_pointers();
self.obj.clear();
}
Expand Down Expand Up @@ -219,7 +215,6 @@ unsafe impl Sync for ReleasePool {}
#[doc(hidden)]
pub struct GILPool {
owned: usize,
borrowed: usize,
// Stable solution for impl !Send
no_send: Unsendable,
}
Expand All @@ -235,7 +230,6 @@ impl GILPool {
pool.release_pointers();
GILPool {
owned: pool.owned.len(),
borrowed: pool.borrowed.len(),
no_send: Unsendable::default(),
}
}
Expand All @@ -248,7 +242,7 @@ impl Drop for GILPool {
fn drop(&mut self) {
unsafe {
let pool = POOL.get_or_init();
pool.drain(self.python(), self.owned, self.borrowed);
pool.drain(self.python(), self.owned);
}
decrement_gil_count();
}
Expand All @@ -275,14 +269,9 @@ pub unsafe fn register_pointer(obj: NonNull<ffi::PyObject>) {
}
}

pub unsafe fn register_owned(_py: Python, obj: NonNull<ffi::PyObject>) -> &PyAny {
pub unsafe fn register_owned(_py: Python, obj: NonNull<ffi::PyObject>) {
let pool = POOL.get_or_init();
&*(pool.owned.push_back(obj) as *const _ as *const PyAny)
}

pub unsafe fn register_borrowed(_py: Python, obj: NonNull<ffi::PyObject>) -> &PyAny {
let pool = POOL.get_or_init();
&*(pool.borrowed.push_back(obj) as *const _ as *const PyAny)
pool.owned.push(obj);
}

/// Increment pyo3's internal GIL count - to be called whenever GILPool or GILGuard is created.
Expand All @@ -304,70 +293,10 @@ fn decrement_gil_count() {
})
}

use self::array_list::ArrayList;

mod array_list {
use std::collections::LinkedList;
const BLOCK_SIZE: usize = 256;

/// A container type for Release Pool
/// See #271 for why this is crated
pub(super) struct ArrayList<T> {
inner: LinkedList<[Option<T>; BLOCK_SIZE]>,
length: usize,
}

impl<T: Copy> ArrayList<T> {
pub fn new() -> Self {
ArrayList {
inner: LinkedList::new(),
length: 0,
}
}
pub fn push_back(&mut self, item: T) -> &T {
let next_idx = self.next_idx();
if next_idx == 0 {
self.inner.push_back([None; BLOCK_SIZE]);
}
self.inner.back_mut().unwrap()[next_idx] = Some(item);
self.length += 1;
self.inner.back().unwrap()[next_idx].as_ref().unwrap()
}
pub fn pop_back(&mut self) -> Option<T> {
self.length -= 1;
let current_idx = self.next_idx();
if current_idx == 0 {
let last_list = self.inner.pop_back()?;
return last_list[0];
}
self.inner.back().and_then(|arr| arr[current_idx])
}
pub fn len(&self) -> usize {
self.length
}
pub fn truncate(&mut self, new_len: usize) {
if self.length <= new_len {
return;
}
while self.inner.len() > (new_len + BLOCK_SIZE - 1) / BLOCK_SIZE {
self.inner.pop_back();
}
self.length = new_len;
}
fn next_idx(&self) -> usize {
self.length % BLOCK_SIZE
}
}
}

#[cfg(test)]
mod test {
use super::{GILPool, NonNull, GIL_COUNT, POOL};
use crate::object::PyObject;
use crate::AsPyPointer;
use crate::Python;
use crate::ToPyObject;
use crate::{ffi, gil};
use super::{GILPool, GIL_COUNT, POOL};
use crate::{ffi, AsPyPointer, PyObject, Python, ToPyObject};

fn get_object() -> PyObject {
// Convenience function for getting a single unique object
Expand All @@ -379,129 +308,6 @@ mod test {
obj.to_object(py)
}

#[test]
fn test_owned() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object();
let obj_ptr = obj.as_ptr();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);

unsafe {
let p = POOL.get_or_init();

{
let gil = Python::acquire_gil();
let py = gil.python();
let _ = gil::register_owned(py, obj.into_nonnull());

assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
assert_eq!(p.owned.len(), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.owned.len(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}

#[test]
fn test_owned_nested() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
let obj_ptr = obj.as_ptr();

unsafe {
let p = POOL.get_or_init();

{
let _pool = GILPool::new();
assert_eq!(p.owned.len(), 0);

let _ = gil::register_owned(py, obj.into_nonnull());

assert_eq!(p.owned.len(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
{
let _pool = GILPool::new();
let obj = get_object();
let _ = gil::register_owned(py, obj.into_nonnull());
assert_eq!(p.owned.len(), 2);
}
assert_eq!(p.owned.len(), 1);
}
{
assert_eq!(p.owned.len(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}

#[test]
fn test_borrowed() {
unsafe {
let p = POOL.get_or_init();

let obj = get_object();
let obj_ptr = obj.as_ptr();
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);

gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());

assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}

#[test]
fn test_borrowed_nested() {
unsafe {
let p = POOL.get_or_init();

let obj = get_object();
let obj_ptr = obj.as_ptr();
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);

gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());

assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);

{
let _pool = GILPool::new();
assert_eq!(p.borrowed.len(), 1);
gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());
assert_eq!(p.borrowed.len(), 2);
}

assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}

#[test]
fn test_pyobject_drop_with_gil_decreases_refcnt() {
let gil = Python::acquire_gil();
Expand Down
15 changes: 12 additions & 3 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::err::{PyErr, PyResult};
use crate::gil;
use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::type_object::{PyBorrowFlagLayout, PyDowncastImpl};
use crate::type_object::PyBorrowFlagLayout;
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyCell, PyClass,
PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
Expand All @@ -21,6 +21,15 @@ pub unsafe trait PyNativeType: Sized {
fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
}
/// Cast `&PyAny` to `&Self` without no type checking.
///
/// # Safety
///
/// Unless obj is not an instance of a type corresponding to Self,
/// this method causes undefined behavior.
unsafe fn unchecked_downcast(obj: &PyAny) -> &Self {
&*(obj.as_ptr() as *const Self)
}
}

/// A Python object of known type.
Expand Down Expand Up @@ -176,8 +185,8 @@ where
{
type Target = T::AsRefTarget;
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p Self::Target {
let any = self as *const Py<T> as *const PyAny;
unsafe { PyDowncastImpl::unchecked_downcast(&*any) }
let any = self.as_ptr() as *const PyAny;
unsafe { PyNativeType::unchecked_downcast(&*any) }
}
}

Expand Down
Loading

0 comments on commit eaee671

Please sign in to comment.